From 357080b7e7e6974aa1cd4dd81f1084dc6d86d4f9 Mon Sep 17 00:00:00 2001 From: KanagarajNN <32181070+KanagarajNN@users.noreply.github.com> Date: Sun, 23 Aug 2020 14:43:00 -0700 Subject: [PATCH 01/27] Fixing a typo --- Part 1/Section 02 - A Quick Refresher/07 - Classes.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Part 1/Section 02 - A Quick Refresher/07 - Classes.ipynb b/Part 1/Section 02 - A Quick Refresher/07 - Classes.ipynb index 5e46359d..af2865b3 100644 --- a/Part 1/Section 02 - A Quick Refresher/07 - Classes.ipynb +++ b/Part 1/Section 02 - A Quick Refresher/07 - Classes.ipynb @@ -1125,7 +1125,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "In Pytyhon we can use some special **decorators** (more on those later) to encapsulate our property getters and setters:" + "In Python we can use some special **decorators** (more on those later) to encapsulate our property getters and setters:" ] }, { From 7cbd742551088227c299d2fd9701738bcea456c3 Mon Sep 17 00:00:00 2001 From: Fred Baptiste <___> Date: Sun, 15 Nov 2020 20:49:06 -0700 Subject: [PATCH 02/27] added MappingProxyType --- .../01 - MappingProxyType.ipynb | 371 ++++++++++++++++++ 1 file changed, 371 insertions(+) create mode 100644 Part 3/Section 11 - Extras/01 - MappingProxyType.ipynb diff --git a/Part 3/Section 11 - Extras/01 - MappingProxyType.ipynb b/Part 3/Section 11 - Extras/01 - MappingProxyType.ipynb new file mode 100644 index 00000000..07ce4485 --- /dev/null +++ b/Part 3/Section 11 - Extras/01 - MappingProxyType.ipynb @@ -0,0 +1,371 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### MappingProxyType" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The mapping proxy type is an easy way to create a read-only **view** of any dictionary.\n", + "\n", + "This can be handy if you want to pass a dictionary around, and have that view reflect the underlying dictionary (even if it is mutated), but not allow the receiver to be able to modify the dictionary." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In fact, this is used by classes all the time:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "class Test:\n", + " a = 100" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "mappingproxy({'__module__': '__main__',\n", + " 'a': 100,\n", + " '__dict__': ,\n", + " '__weakref__': ,\n", + " '__doc__': None})" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Test.__dict__" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As you can see, what is returned here is not actually a `dict` object, but a `mappingproxy`." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To create a mapping proxy from a dictionary we use the `MappingProxyType` from the `types` module:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "from types import MappingProxyType" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "d = {'a': 1, 'b': 2}" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "mp = MappingProxyType(d)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This mapping proxy still behaves like a dictionary:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['a', 'b']" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list(mp.keys())" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[1, 2]" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list(mp.values())" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[('a', 1), ('b', 2)]" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list(mp.items())" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "mp.get('a', 'not found')" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'not found'" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "mp.get('c', 'not found')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "But we cannot mutate it:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "TypeError: 'mappingproxy' object does not support item assignment\n" + ] + } + ], + "source": [ + "try:\n", + " mp['a'] = 100\n", + "except TypeError as ex:\n", + " print('TypeError: ', ex)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "On the other hand, if the underlying dictionary is mutated:" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "d['a'] = 100\n", + "d['c'] = 'new item'" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'a': 100, 'b': 2, 'c': 'new item'}" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "d" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "mappingproxy({'a': 100, 'b': 2, 'c': 'new item'})" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "mp" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And as you can see, the mapping proxy \"sees\" the changes in the undelying dictionary - so it behaves like a view, in the same way `keys()`, `values()` and `items()` do." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You can obtain a **shallow** copy of the proxy by using the `copy()` method:" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [], + "source": [ + "cp = mp.copy()" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'a': 100, 'b': 2, 'c': 'new item'}" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "cp" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As you can see, `cp` is a plain `dict`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.4" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} From d4caa83d0276df066b3d817f468dd8ff59930b20 Mon Sep 17 00:00:00 2001 From: SivaMMurugan <63388297+SivaMMurugan@users.noreply.github.com> Date: Mon, 15 Feb 2021 08:07:21 +0530 Subject: [PATCH 03/27] Typo fix --- .../03 - Lambdas and Sorting.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Part 1/Section 06 - First-Class Functions/03 - Lambdas and Sorting.ipynb b/Part 1/Section 06 - First-Class Functions/03 - Lambdas and Sorting.ipynb index f60ad05a..deff2b3f 100644 --- a/Part 1/Section 06 - First-Class Functions/03 - Lambdas and Sorting.ipynb +++ b/Part 1/Section 06 - First-Class Functions/03 - Lambdas and Sorting.ipynb @@ -60,7 +60,7 @@ "\n", "What if we wanted to make a case-insensitive sort?\n", "\n", - "Python's **sorted** function kas a keyword-only argument that allows us to modify the values that are used to sort the list." + "Python's **sorted** function has a keyword-only argument that allows us to modify the values that are used to sort the list." ] }, { From 9562bed7da13d83fbda5f2d03d09f725d4545817 Mon Sep 17 00:00:00 2001 From: chandru shane <62532606+chandru-shane@users.noreply.github.com> Date: Sat, 11 Dec 2021 17:36:05 +0530 Subject: [PATCH 04/27] Update 11 - Metaprogramming Application 1.ipynb --- .../11 - Metaprogramming Application 1.ipynb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Part 4/Section 14 - Metaprogramming/11 - Metaprogramming Application 1.ipynb b/Part 4/Section 14 - Metaprogramming/11 - Metaprogramming Application 1.ipynb index f3c0027e..7221d59c 100644 --- a/Part 4/Section 14 - Metaprogramming/11 - Metaprogramming Application 1.ipynb +++ b/Part 4/Section 14 - Metaprogramming/11 - Metaprogramming Application 1.ipynb @@ -36,7 +36,7 @@ " return self._y\n", " \n", " def __eq__(self, other):\n", - " return isinstance(other, Point) and (self.x, self.y) == (other.x, other.y)\n", + " return isinstance(other, Point2D) and (self.x, self.y) == (other.x, other.y)\n", " \n", " def __hash__(self):\n", " return hash((self.x, self.y))\n", @@ -68,7 +68,7 @@ " return self._z\n", " \n", " def __eq__(self, other):\n", - " return isinstance(other, Point) and (self.x, self.y, self.z) == (other.x, other.y, other.z)\n", + " return isinstance(other, Point3D) and (self.x, self.y, self.z) == (other.x, other.y, other.z)\n", " \n", " def __hash__(self):\n", " return hash((self.x, self.y, self.z))\n", From 398d715e810dd93fea24249a100b515e18206cc1 Mon Sep 17 00:00:00 2001 From: fbaptiste <6551123+fbaptiste@users.noreply.github.com> Date: Sun, 20 Feb 2022 22:20:42 -0700 Subject: [PATCH 05/27] added Pipfile --- Pipfile | 12 + Pipfile.lock | 803 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 815 insertions(+) create mode 100644 Pipfile create mode 100644 Pipfile.lock diff --git a/Pipfile b/Pipfile new file mode 100644 index 00000000..9faacc84 --- /dev/null +++ b/Pipfile @@ -0,0 +1,12 @@ +[[source]] +url = "https://pypi.org/simple" +verify_ssl = true +name = "pypi" + +[packages] +jupyter = "*" + +[dev-packages] + +[requires] +python_version = "3.9" diff --git a/Pipfile.lock b/Pipfile.lock new file mode 100644 index 00000000..65cade01 --- /dev/null +++ b/Pipfile.lock @@ -0,0 +1,803 @@ +{ + "_meta": { + "hash": { + "sha256": "3ff35502dbd4d7f84d0000d0a78dfba5c47b879759484b6ca31807310dcf969c" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3.9" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "appnope": { + "hashes": [ + "sha256:93aa393e9d6c54c5cd570ccadd8edad61ea0c4b9ea7a01409020c9aa019eb442", + "sha256:dd83cd4b5b460958838f6eb3000c660b1f9caf2a5b1de4264e941512f603258a" + ], + "markers": "platform_system == 'Darwin'", + "version": "==0.1.2" + }, + "argon2-cffi": { + "hashes": [ + "sha256:8c976986f2c5c0e5000919e6de187906cfd81fb1c72bf9d88c01177e77da7f80", + "sha256:d384164d944190a7dd7ef22c6aa3ff197da12962bd04b17f64d4e93d934dba5b" + ], + "markers": "python_version >= '3.6'", + "version": "==21.3.0" + }, + "argon2-cffi-bindings": { + "hashes": [ + "sha256:20ef543a89dee4db46a1a6e206cd015360e5a75822f76df533845c3cbaf72670", + "sha256:2c3e3cc67fdb7d82c4718f19b4e7a87123caf8a93fde7e23cf66ac0337d3cb3f", + "sha256:3b9ef65804859d335dc6b31582cad2c5166f0c3e7975f324d9ffaa34ee7e6583", + "sha256:3e385d1c39c520c08b53d63300c3ecc28622f076f4c2b0e6d7e796e9f6502194", + "sha256:58ed19212051f49a523abb1dbe954337dc82d947fb6e5a0da60f7c8471a8476c", + "sha256:5e00316dabdaea0b2dd82d141cc66889ced0cdcbfa599e8b471cf22c620c329a", + "sha256:603ca0aba86b1349b147cab91ae970c63118a0f30444d4bc80355937c950c082", + "sha256:6a22ad9800121b71099d0fb0a65323810a15f2e292f2ba450810a7316e128ee5", + "sha256:8cd69c07dd875537a824deec19f978e0f2078fdda07fd5c42ac29668dda5f40f", + "sha256:93f9bf70084f97245ba10ee36575f0c3f1e7d7724d67d8e5b08e61787c320ed7", + "sha256:9524464572e12979364b7d600abf96181d3541da11e23ddf565a32e70bd4dc0d", + "sha256:b2ef1c30440dbbcba7a5dc3e319408b59676e2e039e2ae11a8775ecf482b192f", + "sha256:b746dba803a79238e925d9046a63aa26bf86ab2a2fe74ce6b009a1c3f5c8f2ae", + "sha256:bb89ceffa6c791807d1305ceb77dbfacc5aa499891d2c55661c6459651fc39e3", + "sha256:bd46088725ef7f58b5a1ef7ca06647ebaf0eb4baff7d1d0d177c6cc8744abd86", + "sha256:ccb949252cb2ab3a08c02024acb77cfb179492d5701c7cbdbfd776124d4d2367", + "sha256:d4966ef5848d820776f5f562a7d45fdd70c2f330c961d0d745b784034bd9f48d", + "sha256:e415e3f62c8d124ee16018e491a009937f8cf7ebf5eb430ffc5de21b900dad93", + "sha256:ed2937d286e2ad0cc79a7087d3c272832865f779430e0cc2b4f3718d3159b0cb", + "sha256:f1152ac548bd5b8bcecfb0b0371f082037e47128653df2e8ba6e914d384f3c3e", + "sha256:f9f8b450ed0547e3d473fdc8612083fd08dd2120d6ac8f73828df9b7d45bb351" + ], + "markers": "python_version >= '3.6'", + "version": "==21.2.0" + }, + "asttokens": { + "hashes": [ + "sha256:0844691e88552595a6f4a4281a9f7f79b8dd45ca4ccea82e5e05b4bbdb76705c", + "sha256:9a54c114f02c7a9480d56550932546a3f1fe71d8a02f1bc7ccd0ee3ee35cf4d5" + ], + "version": "==2.0.5" + }, + "attrs": { + "hashes": [ + "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4", + "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", + "version": "==21.4.0" + }, + "backcall": { + "hashes": [ + "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e", + "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255" + ], + "version": "==0.2.0" + }, + "black": { + "hashes": [ + "sha256:07e5c049442d7ca1a2fc273c79d1aecbbf1bc858f62e8184abe1ad175c4f7cc2", + "sha256:0e21e1f1efa65a50e3960edd068b6ae6d64ad6235bd8bfea116a03b21836af71", + "sha256:1297c63b9e1b96a3d0da2d85d11cd9bf8664251fd69ddac068b98dc4f34f73b6", + "sha256:228b5ae2c8e3d6227e4bde5920d2fc66cc3400fde7bcc74f480cb07ef0b570d5", + "sha256:2d6f331c02f0f40aa51a22e479c8209d37fcd520c77721c034517d44eecf5912", + "sha256:2ff96450d3ad9ea499fc4c60e425a1439c2120cbbc1ab959ff20f7c76ec7e866", + "sha256:3524739d76b6b3ed1132422bf9d82123cd1705086723bc3e235ca39fd21c667d", + "sha256:35944b7100af4a985abfcaa860b06af15590deb1f392f06c8683b4381e8eeaf0", + "sha256:373922fc66676133ddc3e754e4509196a8c392fec3f5ca4486673e685a421321", + "sha256:5fa1db02410b1924b6749c245ab38d30621564e658297484952f3d8a39fce7e8", + "sha256:6f2f01381f91c1efb1451998bd65a129b3ed6f64f79663a55fe0e9b74a5f81fd", + "sha256:742ce9af3086e5bd07e58c8feb09dbb2b047b7f566eb5f5bc63fd455814979f3", + "sha256:7835fee5238fc0a0baf6c9268fb816b5f5cd9b8793423a75e8cd663c48d073ba", + "sha256:8871fcb4b447206904932b54b567923e5be802b9b19b744fdff092bd2f3118d0", + "sha256:a7c0192d35635f6fc1174be575cb7915e92e5dd629ee79fdaf0dcfa41a80afb5", + "sha256:b1a5ed73ab4c482208d20434f700d514f66ffe2840f63a6252ecc43a9bc77e8a", + "sha256:c8226f50b8c34a14608b848dc23a46e5d08397d009446353dad45e04af0c8e28", + "sha256:ccad888050f5393f0d6029deea2a33e5ae371fd182a697313bdbd835d3edaf9c", + "sha256:dae63f2dbf82882fa3b2a3c49c32bffe144970a573cd68d247af6560fc493ae1", + "sha256:e2f69158a7d120fd641d1fa9a921d898e20d52e44a74a6fbbcc570a62a6bc8ab", + "sha256:efbadd9b52c060a8fc3b9658744091cb33c31f830b3f074422ed27bad2b18e8f", + "sha256:f5660feab44c2e3cb24b2419b998846cbb01c23c7fe645fee45087efa3da2d61", + "sha256:fdb8754b453fb15fad3f72cd9cad3e16776f0964d67cf30ebcbf10327a3777a3" + ], + "markers": "python_full_version >= '3.6.2'", + "version": "==22.1.0" + }, + "bleach": { + "hashes": [ + "sha256:0900d8b37eba61a802ee40ac0061f8c2b5dee29c1927dd1d233e075ebf5a71da", + "sha256:4d2651ab93271d1129ac9cbc679f524565cc8a1b791909c4a51eac4446a15994" + ], + "markers": "python_version >= '3.6'", + "version": "==4.1.0" + }, + "cffi": { + "hashes": [ + "sha256:00c878c90cb53ccfaae6b8bc18ad05d2036553e6d9d1d9dbcf323bbe83854ca3", + "sha256:0104fb5ae2391d46a4cb082abdd5c69ea4eab79d8d44eaaf79f1b1fd806ee4c2", + "sha256:06c48159c1abed75c2e721b1715c379fa3200c7784271b3c46df01383b593636", + "sha256:0808014eb713677ec1292301ea4c81ad277b6cdf2fdd90fd540af98c0b101d20", + "sha256:10dffb601ccfb65262a27233ac273d552ddc4d8ae1bf93b21c94b8511bffe728", + "sha256:14cd121ea63ecdae71efa69c15c5543a4b5fbcd0bbe2aad864baca0063cecf27", + "sha256:17771976e82e9f94976180f76468546834d22a7cc404b17c22df2a2c81db0c66", + "sha256:181dee03b1170ff1969489acf1c26533710231c58f95534e3edac87fff06c443", + "sha256:23cfe892bd5dd8941608f93348c0737e369e51c100d03718f108bf1add7bd6d0", + "sha256:263cc3d821c4ab2213cbe8cd8b355a7f72a8324577dc865ef98487c1aeee2bc7", + "sha256:2756c88cbb94231c7a147402476be2c4df2f6078099a6f4a480d239a8817ae39", + "sha256:27c219baf94952ae9d50ec19651a687b826792055353d07648a5695413e0c605", + "sha256:2a23af14f408d53d5e6cd4e3d9a24ff9e05906ad574822a10563efcef137979a", + "sha256:31fb708d9d7c3f49a60f04cf5b119aeefe5644daba1cd2a0fe389b674fd1de37", + "sha256:3415c89f9204ee60cd09b235810be700e993e343a408693e80ce7f6a40108029", + "sha256:3773c4d81e6e818df2efbc7dd77325ca0dcb688116050fb2b3011218eda36139", + "sha256:3b96a311ac60a3f6be21d2572e46ce67f09abcf4d09344c49274eb9e0bf345fc", + "sha256:3f7d084648d77af029acb79a0ff49a0ad7e9d09057a9bf46596dac9514dc07df", + "sha256:41d45de54cd277a7878919867c0f08b0cf817605e4eb94093e7516505d3c8d14", + "sha256:4238e6dab5d6a8ba812de994bbb0a79bddbdf80994e4ce802b6f6f3142fcc880", + "sha256:45db3a33139e9c8f7c09234b5784a5e33d31fd6907800b316decad50af323ff2", + "sha256:45e8636704eacc432a206ac7345a5d3d2c62d95a507ec70d62f23cd91770482a", + "sha256:4958391dbd6249d7ad855b9ca88fae690783a6be9e86df65865058ed81fc860e", + "sha256:4a306fa632e8f0928956a41fa8e1d6243c71e7eb59ffbd165fc0b41e316b2474", + "sha256:57e9ac9ccc3101fac9d6014fba037473e4358ef4e89f8e181f8951a2c0162024", + "sha256:59888172256cac5629e60e72e86598027aca6bf01fa2465bdb676d37636573e8", + "sha256:5e069f72d497312b24fcc02073d70cb989045d1c91cbd53979366077959933e0", + "sha256:64d4ec9f448dfe041705426000cc13e34e6e5bb13736e9fd62e34a0b0c41566e", + "sha256:6dc2737a3674b3e344847c8686cf29e500584ccad76204efea14f451d4cc669a", + "sha256:74fdfdbfdc48d3f47148976f49fab3251e550a8720bebc99bf1483f5bfb5db3e", + "sha256:75e4024375654472cc27e91cbe9eaa08567f7fbdf822638be2814ce059f58032", + "sha256:786902fb9ba7433aae840e0ed609f45c7bcd4e225ebb9c753aa39725bb3e6ad6", + "sha256:8b6c2ea03845c9f501ed1313e78de148cd3f6cad741a75d43a29b43da27f2e1e", + "sha256:91d77d2a782be4274da750752bb1650a97bfd8f291022b379bb8e01c66b4e96b", + "sha256:91ec59c33514b7c7559a6acda53bbfe1b283949c34fe7440bcf917f96ac0723e", + "sha256:920f0d66a896c2d99f0adbb391f990a84091179542c205fa53ce5787aff87954", + "sha256:a5263e363c27b653a90078143adb3d076c1a748ec9ecc78ea2fb916f9b861962", + "sha256:abb9a20a72ac4e0fdb50dae135ba5e77880518e742077ced47eb1499e29a443c", + "sha256:c2051981a968d7de9dd2d7b87bcb9c939c74a34626a6e2f8181455dd49ed69e4", + "sha256:c21c9e3896c23007803a875460fb786118f0cdd4434359577ea25eb556e34c55", + "sha256:c2502a1a03b6312837279c8c1bd3ebedf6c12c4228ddbad40912d671ccc8a962", + "sha256:d4d692a89c5cf08a8557fdeb329b82e7bf609aadfaed6c0d79f5a449a3c7c023", + "sha256:da5db4e883f1ce37f55c667e5c0de439df76ac4cb55964655906306918e7363c", + "sha256:e7022a66d9b55e93e1a845d8c9eba2a1bebd4966cd8bfc25d9cd07d515b33fa6", + "sha256:ef1f279350da2c586a69d32fc8733092fd32cc8ac95139a00377841f59a3f8d8", + "sha256:f54a64f8b0c8ff0b64d18aa76675262e1700f3995182267998c31ae974fbc382", + "sha256:f5c7150ad32ba43a07c4479f40241756145a1f03b43480e058cfd862bf5041c7", + "sha256:f6f824dc3bce0edab5f427efcfb1d63ee75b6fcb7282900ccaf925be84efb0fc", + "sha256:fd8a250edc26254fe5b33be00402e6d287f562b6a5b2152dec302fa15bb3e997", + "sha256:ffaa5c925128e29efbde7301d8ecaf35c8c60ffbcd6a1ffd3a552177c8e5e796" + ], + "version": "==1.15.0" + }, + "click": { + "hashes": [ + "sha256:6a7a62563bbfabfda3a38f3023a1db4a35978c0abd76f6c9605ecd6554d6d9b1", + "sha256:8458d7b1287c5fb128c90e23381cf99dcde74beaf6c7ff6384ce84d6fe090adb" + ], + "markers": "python_version >= '3.6'", + "version": "==8.0.4" + }, + "debugpy": { + "hashes": [ + "sha256:01e98c594b3e66d529e40edf314f849cd1a21f7a013298df58cd8e263bf8e184", + "sha256:16db27b4b91991442f91d73604d32080b30de655aca9ba821b1972ea8171021b", + "sha256:17a25ce9d7714f92fc97ef00cc06269d7c2b163094990ada30156ed31d9a5030", + "sha256:194f95dd3e84568b5489aab5689a3a2c044e8fdc06f1890b8b4f70b6b89f2778", + "sha256:1ec3a086e14bba6c472632025b8fe5bdfbaef2afa1ebd5c6615ce6ed8d89bc67", + "sha256:23df67fc56d59e386c342428a7953c2c06cc226d8525b11319153e96afb65b0c", + "sha256:26fbe53cca45a608679094791ce587b6e2798acd1d4777a8b303b07622e85182", + "sha256:2b073ad5e8d8c488fbb6a116986858bab0c9c4558f28deb8832c7a5a27405bd6", + "sha256:318f81f37341e4e054b4267d39896b73cddb3612ca13b39d7eea45af65165e1d", + "sha256:3a457ad9c0059a21a6c7d563c1f18e924f5cf90278c722bd50ede6f56b77c7fe", + "sha256:4404a62fb5332ea5c8c9132290eef50b3a0ba38cecacad5529e969a783bcbdd7", + "sha256:5d76a4fd028d8009c3faf1185b4b78ceb2273dd2499447664b03939e0368bb90", + "sha256:70b422c63a833630c33e3f9cdbd9b6971f8c5afd452697e464339a21bbe862ba", + "sha256:82f5f9ce93af6861a0713f804e62ab390bb12a17f113153e47fea8bbb1dfbe36", + "sha256:a2aa64f6d2ca7ded8a7e8a4e7cae3bc71866b09876b7b05cecad231779cb9156", + "sha256:b2df2c373e85871086bd55271c929670cd4e1dba63e94a08d442db830646203b", + "sha256:b5b3157372e0e0a1297a8b6b5280bcf1d35a40f436c7973771c972726d1e32d5", + "sha256:d2b09e91fbd1efa4f4fda121d49af89501beda50c18ed7499712c71a4bf3452e", + "sha256:d876db8c312eeb02d85611e0f696abe66a2c1515e6405943609e725d5ff36f2a", + "sha256:f3a3dca9104aa14fd4210edcce6d9ce2b65bd9618c0b222135a40b9d6e2a9eeb", + "sha256:f73988422b17f071ad3c4383551ace1ba5ed810cbab5f9c362783d22d40a08dc" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", + "version": "==1.5.1" + }, + "decorator": { + "hashes": [ + "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330", + "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186" + ], + "markers": "python_version >= '3.5'", + "version": "==5.1.1" + }, + "defusedxml": { + "hashes": [ + "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69", + "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", + "version": "==0.7.1" + }, + "entrypoints": { + "hashes": [ + "sha256:b706eddaa9218a19ebcd67b56818f05bb27589b1ca9e8d797b74affad4ccacd4", + "sha256:f174b5ff827504fd3cd97cc3f8649f3693f51538c7e4bdf3ef002c8429d42f9f" + ], + "markers": "python_version >= '3.6'", + "version": "==0.4" + }, + "executing": { + "hashes": [ + "sha256:32fc6077b103bd19e6494a72682d66d5763cf20a106d5aa7c5ccbea4e47b0df7", + "sha256:c23bf42e9a7b9b212f185b1b2c3c91feb895963378887bb10e64a2e612ec0023" + ], + "version": "==0.8.2" + }, + "ipykernel": { + "hashes": [ + "sha256:4fae9df6e192837552b2406a6052d707046dd2e153860be73c68484bacba18ed", + "sha256:f95070a2dfd3147f8ab19f18ee46733310813758593745e07ec18fb08b409f1d" + ], + "markers": "python_version >= '3.7'", + "version": "==6.9.1" + }, + "ipython": { + "hashes": [ + "sha256:ab564d4521ea8ceaac26c3a2c6e5ddbca15c8848fd5a5cc325f960da88d42974", + "sha256:c503a0dd6ccac9c8c260b211f2dd4479c042b49636b097cc9a0d55fe62dff64c" + ], + "markers": "python_version >= '3.8'", + "version": "==8.0.1" + }, + "ipython-genutils": { + "hashes": [ + "sha256:72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8", + "sha256:eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8" + ], + "version": "==0.2.0" + }, + "ipywidgets": { + "hashes": [ + "sha256:00974f7cb4d5f8d494c19810fedb9fa9b64bffd3cda7c2be23c133a1ad3c99c5", + "sha256:d258f582f915c62ea91023299603be095de19afb5ee271698f88327b9fe9bf43" + ], + "version": "==7.6.5" + }, + "jedi": { + "hashes": [ + "sha256:637c9635fcf47945ceb91cd7f320234a7be540ded6f3e99a50cb6febdfd1ba8d", + "sha256:74137626a64a99c8eb6ae5832d99b3bdd7d29a3850fe2aa80a4126b2a7d949ab" + ], + "markers": "python_version >= '3.6'", + "version": "==0.18.1" + }, + "jinja2": { + "hashes": [ + "sha256:077ce6014f7b40d03b47d1f1ca4b0fc8328a692bd284016f806ed0eaca390ad8", + "sha256:611bb273cd68f3b993fabdc4064fc858c5b47a973cb5aa7999ec1ba405c87cd7" + ], + "markers": "python_version >= '3.6'", + "version": "==3.0.3" + }, + "jsonschema": { + "hashes": [ + "sha256:636694eb41b3535ed608fe04129f26542b59ed99808b4f688aa32dcf55317a83", + "sha256:77281a1f71684953ee8b3d488371b162419767973789272434bbc3f29d9c8823" + ], + "markers": "python_version >= '3.7'", + "version": "==4.4.0" + }, + "jupyter": { + "hashes": [ + "sha256:3e1f86076bbb7c8c207829390305a2b1fe836d471ed54be66a3b8c41e7f46cc7", + "sha256:5b290f93b98ffbc21c0c7e749f054b3267782166d72fa5e3ed1ed4eaf34a2b78", + "sha256:d9dc4b3318f310e34c82951ea5d6683f67bed7def4b259fafbfe4f1beb1d8e5f" + ], + "index": "pypi", + "version": "==1.0.0" + }, + "jupyter-client": { + "hashes": [ + "sha256:4ea61033726c8e579edb55626d8ee2e6bf0a83158ddf3751b8dd46b2c5cd1e96", + "sha256:d56f1c57bef42ff31e61b1185d3348a5b2bcde7c9a05523ae4dbe5ee0871797c" + ], + "markers": "python_full_version >= '3.6.1'", + "version": "==7.1.2" + }, + "jupyter-console": { + "hashes": [ + "sha256:242248e1685039cd8bff2c2ecb7ce6c1546eb50ee3b08519729e6e881aec19c7", + "sha256:7799c4ea951e0e96ba8260575423cb323ea5a03fcf5503560fa3e15748869e27" + ], + "markers": "python_version >= '3.6'", + "version": "==6.4.0" + }, + "jupyter-core": { + "hashes": [ + "sha256:d69baeb9ffb128b8cd2657fcf2703f89c769d1673c851812119e3a2a0e93ad9a", + "sha256:f875e4d27e202590311d468fa55f90c575f201490bd0c18acabe4e318db4a46d" + ], + "markers": "python_version >= '3.6'", + "version": "==4.9.2" + }, + "jupyterlab-pygments": { + "hashes": [ + "sha256:abfb880fd1561987efaefcb2d2ac75145d2a5d0139b1876d5be806e32f630008", + "sha256:cfcda0873626150932f438eccf0f8bf22bfa92345b814890ab360d666b254146" + ], + "version": "==0.1.2" + }, + "jupyterlab-widgets": { + "hashes": [ + "sha256:7885092b2b96bf189c3a705cc3c412a4472ec5e8382d0b47219a66cccae73cfa", + "sha256:f5d9efface8ec62941173ba1cffb2edd0ecddc801c11ae2931e30b50492eb8f7" + ], + "markers": "python_version >= '3.6'", + "version": "==1.0.2" + }, + "markupsafe": { + "hashes": [ + "sha256:023af8c54fe63530545f70dd2a2a7eed18d07a9a77b94e8bf1e2ff7f252db9a3", + "sha256:09c86c9643cceb1d87ca08cdc30160d1b7ab49a8a21564868921959bd16441b8", + "sha256:142119fb14a1ef6d758912b25c4e803c3ff66920635c44078666fe7cc3f8f759", + "sha256:1d1fb9b2eec3c9714dd936860850300b51dbaa37404209c8d4cb66547884b7ed", + "sha256:204730fd5fe2fe3b1e9ccadb2bd18ba8712b111dcabce185af0b3b5285a7c989", + "sha256:24c3be29abb6b34052fd26fc7a8e0a49b1ee9d282e3665e8ad09a0a68faee5b3", + "sha256:290b02bab3c9e216da57c1d11d2ba73a9f73a614bbdcc027d299a60cdfabb11a", + "sha256:3028252424c72b2602a323f70fbf50aa80a5d3aa616ea6add4ba21ae9cc9da4c", + "sha256:30c653fde75a6e5eb814d2a0a89378f83d1d3f502ab710904ee585c38888816c", + "sha256:3cace1837bc84e63b3fd2dfce37f08f8c18aeb81ef5cf6bb9b51f625cb4e6cd8", + "sha256:4056f752015dfa9828dce3140dbadd543b555afb3252507348c493def166d454", + "sha256:454ffc1cbb75227d15667c09f164a0099159da0c1f3d2636aa648f12675491ad", + "sha256:598b65d74615c021423bd45c2bc5e9b59539c875a9bdb7e5f2a6b92dfcfc268d", + "sha256:599941da468f2cf22bf90a84f6e2a65524e87be2fce844f96f2dd9a6c9d1e635", + "sha256:5ddea4c352a488b5e1069069f2f501006b1a4362cb906bee9a193ef1245a7a61", + "sha256:62c0285e91414f5c8f621a17b69fc0088394ccdaa961ef469e833dbff64bd5ea", + "sha256:679cbb78914ab212c49c67ba2c7396dc599a8479de51b9a87b174700abd9ea49", + "sha256:6e104c0c2b4cd765b4e83909cde7ec61a1e313f8a75775897db321450e928cce", + "sha256:736895a020e31b428b3382a7887bfea96102c529530299f426bf2e636aacec9e", + "sha256:75bb36f134883fdbe13d8e63b8675f5f12b80bb6627f7714c7d6c5becf22719f", + "sha256:7d2f5d97fcbd004c03df8d8fe2b973fe2b14e7bfeb2cfa012eaa8759ce9a762f", + "sha256:80beaf63ddfbc64a0452b841d8036ca0611e049650e20afcb882f5d3c266d65f", + "sha256:84ad5e29bf8bab3ad70fd707d3c05524862bddc54dc040982b0dbcff36481de7", + "sha256:8da5924cb1f9064589767b0f3fc39d03e3d0fb5aa29e0cb21d43106519bd624a", + "sha256:961eb86e5be7d0973789f30ebcf6caab60b844203f4396ece27310295a6082c7", + "sha256:96de1932237abe0a13ba68b63e94113678c379dca45afa040a17b6e1ad7ed076", + "sha256:a0a0abef2ca47b33fb615b491ce31b055ef2430de52c5b3fb19a4042dbc5cadb", + "sha256:b2a5a856019d2833c56a3dcac1b80fe795c95f401818ea963594b345929dffa7", + "sha256:b8811d48078d1cf2a6863dafb896e68406c5f513048451cd2ded0473133473c7", + "sha256:c532d5ab79be0199fa2658e24a02fce8542df196e60665dd322409a03db6a52c", + "sha256:d3b64c65328cb4cd252c94f83e66e3d7acf8891e60ebf588d7b493a55a1dbf26", + "sha256:d4e702eea4a2903441f2735799d217f4ac1b55f7d8ad96ab7d4e25417cb0827c", + "sha256:d5653619b3eb5cbd35bfba3c12d575db2a74d15e0e1c08bf1db788069d410ce8", + "sha256:d66624f04de4af8bbf1c7f21cc06649c1c69a7f84109179add573ce35e46d448", + "sha256:e67ec74fada3841b8c5f4c4f197bea916025cb9aa3fe5abf7d52b655d042f956", + "sha256:e6f7f3f41faffaea6596da86ecc2389672fa949bd035251eab26dc6697451d05", + "sha256:f02cf7221d5cd915d7fa58ab64f7ee6dd0f6cddbb48683debf5d04ae9b1c2cc1", + "sha256:f0eddfcabd6936558ec020130f932d479930581171368fd728efcfb6ef0dd357", + "sha256:fabbe18087c3d33c5824cb145ffca52eccd053061df1d79d4b66dafa5ad2a5ea", + "sha256:fc3150f85e2dbcf99e65238c842d1cfe69d3e7649b19864c1cc043213d9cd730" + ], + "markers": "python_version >= '3.7'", + "version": "==2.1.0" + }, + "matplotlib-inline": { + "hashes": [ + "sha256:a04bfba22e0d1395479f866853ec1ee28eea1485c1d69a6faf00dc3e24ff34ee", + "sha256:aed605ba3b72462d64d475a21a9296f400a19c4f74a31b59103d2a99ffd5aa5c" + ], + "markers": "python_version >= '3.5'", + "version": "==0.1.3" + }, + "mistune": { + "hashes": [ + "sha256:59a3429db53c50b5c6bcc8a07f8848cb00d7dc8bdb431a4ab41920d201d4756e", + "sha256:88a1051873018da288eee8538d476dffe1262495144b33ecb586c4ab266bb8d4" + ], + "version": "==0.8.4" + }, + "mypy-extensions": { + "hashes": [ + "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d", + "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8" + ], + "version": "==0.4.3" + }, + "nbclient": { + "hashes": [ + "sha256:03e857bea3012377289daa1e1c1651f4fc0295bcd109ccd36a337efcdbebaed7", + "sha256:751516992f34b58172bad54eef1e4bf7e4f4460d58e255ca1a4e5c9649476007" + ], + "markers": "python_version >= '3.7'", + "version": "==0.5.11" + }, + "nbconvert": { + "hashes": [ + "sha256:7b006ae9979af56200e7fa3db39d9d12c99e811e8843b05dbe518e5b754bcb2e", + "sha256:eb2803db18f6facce6bf3b01b684fe47907994bd156d15eaccdf011e3d7f8164" + ], + "markers": "python_version >= '3.7'", + "version": "==6.4.2" + }, + "nbformat": { + "hashes": [ + "sha256:b516788ad70771c6250977c1374fcca6edebe6126fd2adb5a69aa5c2356fd1c8", + "sha256:eb8447edd7127d043361bc17f2f5a807626bc8e878c7709a1c647abda28a9171" + ], + "markers": "python_version >= '3.5'", + "version": "==5.1.3" + }, + "nest-asyncio": { + "hashes": [ + "sha256:3fdd0d6061a2bb16f21fe8a9c6a7945be83521d81a0d15cff52e9edee50101d6", + "sha256:f969f6013a16fadb4adcf09d11a68a4f617c6049d7af7ac2c676110169a63abd" + ], + "markers": "python_version >= '3.5'", + "version": "==1.5.4" + }, + "notebook": { + "hashes": [ + "sha256:1e985c9dc6f678bdfffb9dc657306b5469bfa62d73e03f74e8defbf76d284312", + "sha256:3e702fcc54b8ae597533c3864793b7a1e971dec9e112f67235828d8a798fd654" + ], + "markers": "python_version >= '3.6'", + "version": "==6.4.8" + }, + "packaging": { + "hashes": [ + "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb", + "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522" + ], + "markers": "python_version >= '3.6'", + "version": "==21.3" + }, + "pandocfilters": { + "hashes": [ + "sha256:0b679503337d233b4339a817bfc8c50064e2eff681314376a47cb582305a7a38", + "sha256:33aae3f25fd1a026079f5d27bdd52496f0e0803b3469282162bafdcbdf6ef14f" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==1.5.0" + }, + "parso": { + "hashes": [ + "sha256:8c07be290bb59f03588915921e29e8a50002acaf2cdc5fa0e0114f91709fafa0", + "sha256:c001d4636cd3aecdaf33cbb40aebb59b094be2a74c556778ef5576c175e19e75" + ], + "markers": "python_version >= '3.6'", + "version": "==0.8.3" + }, + "pathspec": { + "hashes": [ + "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a", + "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1" + ], + "version": "==0.9.0" + }, + "pexpect": { + "hashes": [ + "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937", + "sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c" + ], + "markers": "sys_platform != 'win32'", + "version": "==4.8.0" + }, + "pickleshare": { + "hashes": [ + "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca", + "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56" + ], + "version": "==0.7.5" + }, + "platformdirs": { + "hashes": [ + "sha256:7535e70dfa32e84d4b34996ea99c5e432fa29a708d0f4e394bbcb2a8faa4f16d", + "sha256:bcae7cab893c2d310a711b70b24efb93334febe65f8de776ee320b517471e227" + ], + "markers": "python_version >= '3.7'", + "version": "==2.5.1" + }, + "prometheus-client": { + "hashes": [ + "sha256:357a447fd2359b0a1d2e9b311a0c5778c330cfbe186d880ad5a6b39884652316", + "sha256:ada41b891b79fca5638bd5cfe149efa86512eaa55987893becd2c6d8d0a5dfc5" + ], + "markers": "python_version >= '3.6'", + "version": "==0.13.1" + }, + "prompt-toolkit": { + "hashes": [ + "sha256:30129d870dcb0b3b6a53efdc9d0a83ea96162ffd28ffe077e94215b233dc670c", + "sha256:9f1cd16b1e86c2968f2519d7fb31dd9d669916f515612c269d14e9ed52b51650" + ], + "markers": "python_full_version >= '3.6.2'", + "version": "==3.0.28" + }, + "ptyprocess": { + "hashes": [ + "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", + "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220" + ], + "version": "==0.7.0" + }, + "pure-eval": { + "hashes": [ + "sha256:01eaab343580944bc56080ebe0a674b39ec44a945e6d09ba7db3cb8cec289350", + "sha256:2b45320af6dfaa1750f543d714b6d1c520a1688dec6fd24d339063ce0aaa9ac3" + ], + "version": "==0.2.2" + }, + "pycparser": { + "hashes": [ + "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9", + "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206" + ], + "version": "==2.21" + }, + "pygments": { + "hashes": [ + "sha256:44238f1b60a76d78fc8ca0528ee429702aae011c265fe6a8dd8b63049ae41c65", + "sha256:4e426f72023d88d03b2fa258de560726ce890ff3b630f88c21cbb8b2503b8c6a" + ], + "markers": "python_version >= '3.5'", + "version": "==2.11.2" + }, + "pyparsing": { + "hashes": [ + "sha256:18ee9022775d270c55187733956460083db60b37d0d0fb357445f3094eed3eea", + "sha256:a6c06a88f252e6c322f65faf8f418b16213b51bdfaece0524c1c1bc30c63c484" + ], + "markers": "python_version >= '3.6'", + "version": "==3.0.7" + }, + "pyrsistent": { + "hashes": [ + "sha256:0e3e1fcc45199df76053026a51cc59ab2ea3fc7c094c6627e93b7b44cdae2c8c", + "sha256:1b34eedd6812bf4d33814fca1b66005805d3640ce53140ab8bbb1e2651b0d9bc", + "sha256:4ed6784ceac462a7d6fcb7e9b663e93b9a6fb373b7f43594f9ff68875788e01e", + "sha256:5d45866ececf4a5fff8742c25722da6d4c9e180daa7b405dc0a2a2790d668c26", + "sha256:636ce2dc235046ccd3d8c56a7ad54e99d5c1cd0ef07d9ae847306c91d11b5fec", + "sha256:6455fc599df93d1f60e1c5c4fe471499f08d190d57eca040c0ea182301321286", + "sha256:6bc66318fb7ee012071b2792024564973ecc80e9522842eb4e17743604b5e045", + "sha256:7bfe2388663fd18bd8ce7db2c91c7400bf3e1a9e8bd7d63bf7e77d39051b85ec", + "sha256:7ec335fc998faa4febe75cc5268a9eac0478b3f681602c1f27befaf2a1abe1d8", + "sha256:914474c9f1d93080338ace89cb2acee74f4f666fb0424896fcfb8d86058bf17c", + "sha256:b568f35ad53a7b07ed9b1b2bae09eb15cdd671a5ba5d2c66caee40dbf91c68ca", + "sha256:cdfd2c361b8a8e5d9499b9082b501c452ade8bbf42aef97ea04854f4a3f43b22", + "sha256:d1b96547410f76078eaf66d282ddca2e4baae8964364abb4f4dcdde855cd123a", + "sha256:d4d61f8b993a7255ba714df3aca52700f8125289f84f704cf80916517c46eb96", + "sha256:d7a096646eab884bf8bed965bad63ea327e0d0c38989fc83c5ea7b8a87037bfc", + "sha256:df46c854f490f81210870e509818b729db4488e1f30f2a1ce1698b2295a878d1", + "sha256:e24a828f57e0c337c8d8bb9f6b12f09dfdf0273da25fda9e314f0b684b415a07", + "sha256:e4f3149fd5eb9b285d6bfb54d2e5173f6a116fe19172686797c056672689daf6", + "sha256:e92a52c166426efbe0d1ec1332ee9119b6d32fc1f0bbfd55d5c1088070e7fc1b", + "sha256:f87cc2863ef33c709e237d4b5f4502a62a00fab450c9e020892e8e2ede5847f5", + "sha256:fd8da6d0124efa2f67d86fa70c851022f87c98e205f0594e1fae044e7119a5a6" + ], + "markers": "python_version >= '3.7'", + "version": "==0.18.1" + }, + "python-dateutil": { + "hashes": [ + "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", + "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'", + "version": "==2.8.2" + }, + "pyzmq": { + "hashes": [ + "sha256:08c4e315a76ef26eb833511ebf3fa87d182152adf43dedee8d79f998a2162a0b", + "sha256:0ca6cd58f62a2751728016d40082008d3b3412a7f28ddfb4a2f0d3c130f69e74", + "sha256:1621e7a2af72cced1f6ec8ca8ca91d0f76ac236ab2e8828ac8fe909512d566cb", + "sha256:18cd854b423fce44951c3a4d3e686bac8f1243d954f579e120a1714096637cc0", + "sha256:2841997a0d85b998cbafecb4183caf51fd19c4357075dfd33eb7efea57e4c149", + "sha256:2b97502c16a5ec611cd52410bdfaab264997c627a46b0f98d3f666227fd1ea2d", + "sha256:3a4c9886d61d386b2b493377d980f502186cd71d501fffdba52bd2a0880cef4f", + "sha256:3c1895c95be92600233e476fe283f042e71cf8f0b938aabf21b7aafa62a8dac9", + "sha256:42abddebe2c6a35180ca549fadc7228d23c1e1f76167c5ebc8a936b5804ea2df", + "sha256:468bd59a588e276961a918a3060948ae68f6ff5a7fa10bb2f9160c18fe341067", + "sha256:480b9931bfb08bf8b094edd4836271d4d6b44150da051547d8c7113bf947a8b0", + "sha256:53f4fd13976789ffafedd4d46f954c7bb01146121812b72b4ddca286034df966", + "sha256:62bcade20813796c426409a3e7423862d50ff0639f5a2a95be4b85b09a618666", + "sha256:67db33bea0a29d03e6eeec55a8190e033318cee3cbc732ba8fd939617cbf762d", + "sha256:6b217b8f9dfb6628f74b94bdaf9f7408708cb02167d644edca33f38746ca12dd", + "sha256:7661fc1d5cb73481cf710a1418a4e1e301ed7d5d924f91c67ba84b2a1b89defd", + "sha256:76c532fd68b93998aab92356be280deec5de8f8fe59cd28763d2cc8a58747b7f", + "sha256:79244b9e97948eaf38695f4b8e6fc63b14b78cc37f403c6642ba555517ac1268", + "sha256:7c58f598d9fcc52772b89a92d72bf8829c12d09746a6d2c724c5b30076c1f11d", + "sha256:7dc09198e4073e6015d9a8ea093fc348d4e59de49382476940c3dd9ae156fba8", + "sha256:80e043a89c6cadefd3a0712f8a1322038e819ebe9dbac7eca3bce1721bcb63bf", + "sha256:851977788b9caa8ed011f5f643d3ee8653af02c5fc723fa350db5125abf2be7b", + "sha256:8eddc033e716f8c91c6a2112f0a8ebc5e00532b4a6ae1eb0ccc48e027f9c671c", + "sha256:902319cfe23366595d3fa769b5b751e6ee6750a0a64c5d9f757d624b2ac3519e", + "sha256:954e73c9cd4d6ae319f1c936ad159072b6d356a92dcbbabfd6e6204b9a79d356", + "sha256:ab888624ed68930442a3f3b0b921ad7439c51ba122dbc8c386e6487a658e4a4e", + "sha256:acebba1a23fb9d72b42471c3771b6f2f18dcd46df77482612054bd45c07dfa36", + "sha256:b4ebed0977f92320f6686c96e9e8dd29eed199eb8d066936bac991afc37cbb70", + "sha256:badb868fff14cfd0e200eaa845887b1011146a7d26d579aaa7f966c203736b92", + "sha256:be4e0f229cf3a71f9ecd633566bd6f80d9fa6afaaff5489492be63fe459ef98c", + "sha256:c0f84360dcca3481e8674393bdf931f9f10470988f87311b19d23cda869bb6b7", + "sha256:c1e41b32d6f7f9c26bc731a8b529ff592f31fc8b6ef2be9fa74abd05c8a342d7", + "sha256:c88fa7410e9fc471e0858638f403739ee869924dd8e4ae26748496466e27ac59", + "sha256:cf98fd7a6c8aaa08dbc699ffae33fd71175696d78028281bc7b832b26f00ca57", + "sha256:d072f7dfbdb184f0786d63bda26e8a0882041b1e393fbe98940395f7fab4c5e2", + "sha256:d1b5d457acbadcf8b27561deeaa386b0217f47626b29672fa7bd31deb6e91e1b", + "sha256:d3dcb5548ead4f1123851a5ced467791f6986d68c656bc63bfff1bf9e36671e2", + "sha256:d6157793719de168b199194f6b6173f0ccd3bf3499e6870fac17086072e39115", + "sha256:d728b08448e5ac3e4d886b165385a262883c34b84a7fe1166277fe675e1c197a", + "sha256:de8df0684398bd74ad160afdc2a118ca28384ac6f5e234eb0508858d8d2d9364", + "sha256:e6a02cf7271ee94674a44f4e62aa061d2d049001c844657740e156596298b70b", + "sha256:ea12133df25e3a6918718fbb9a510c6ee5d3fdd5a346320421aac3882f4feeea", + "sha256:ea5a79e808baef98c48c884effce05c31a0698c1057de8fc1c688891043c1ce1", + "sha256:f43b4a2e6218371dd4f41e547bd919ceeb6ebf4abf31a7a0669cd11cd91ea973", + "sha256:f762442bab706fd874064ca218b33a1d8e40d4938e96c24dafd9b12e28017f45", + "sha256:f89468059ebc519a7acde1ee50b779019535db8dcf9b8c162ef669257fef7a93", + "sha256:f907c7359ce8bf7f7e63c82f75ad0223384105f5126f313400b7e8004d9b33c3" + ], + "markers": "python_version >= '3.6'", + "version": "==22.3.0" + }, + "qtconsole": { + "hashes": [ + "sha256:4aa6a3e600e0c8cf16853f2378311bc2371f57cb0f22ecfc28994f4cf409ee2e", + "sha256:8f9db97b27782184efd0a0f2d57ea3bd852d053747a2e442a9011329c082976d" + ], + "markers": "python_version >= '3.6'", + "version": "==5.2.2" + }, + "qtpy": { + "hashes": [ + "sha256:adfd073ffbd2de81dc7aaa0b983499ef5c59c96adcfdcc9dea60d42ca885eb8f", + "sha256:d93f2c98e97387fcc9d623d509772af5b6c15ab9d8f9f4c5dfbad9a73ad34812" + ], + "markers": "python_version >= '3.6'", + "version": "==2.0.1" + }, + "send2trash": { + "hashes": [ + "sha256:d2c24762fd3759860a0aff155e45871447ea58d2be6bdd39b5c8f966a0c99c2d", + "sha256:f20eaadfdb517eaca5ce077640cb261c7d2698385a6a0f072a4a5447fd49fa08" + ], + "version": "==1.8.0" + }, + "setuptools": { + "hashes": [ + "sha256:2347b2b432c891a863acadca2da9ac101eae6169b1d3dfee2ec605ecd50dbfe5", + "sha256:e4f30b9f84e5ab3decf945113119649fec09c1fc3507c6ebffec75646c56e62b" + ], + "markers": "python_version >= '3.7'", + "version": "==60.9.3" + }, + "six": { + "hashes": [ + "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", + "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'", + "version": "==1.16.0" + }, + "stack-data": { + "hashes": [ + "sha256:45692d41bd633a9503a5195552df22b583caf16f0b27c4e58c98d88c8b648e12", + "sha256:999762f9c3132308789affa03e9271bbbe947bf78311851f4d485d8402ed858e" + ], + "version": "==0.2.0" + }, + "terminado": { + "hashes": [ + "sha256:5b82b5c6e991f0705a76f961f43262a7fb1e55b093c16dca83f16384a7f39b7b", + "sha256:f446b522b50a7aa68b5def0a02893978fb48cb82298b0ebdae13003c6ee6f198" + ], + "markers": "python_version >= '3.7'", + "version": "==0.13.1" + }, + "testpath": { + "hashes": [ + "sha256:1acf7a0bcd3004ae8357409fc33751e16d37ccc650921da1094a86581ad1e417", + "sha256:8044f9a0bab6567fc644a3593164e872543bb44225b0e24846e2c89237937589" + ], + "markers": "python_version >= '3.5'", + "version": "==0.5.0" + }, + "tomli": { + "hashes": [ + "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", + "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f" + ], + "markers": "python_version >= '3.7'", + "version": "==2.0.1" + }, + "tornado": { + "hashes": [ + "sha256:0a00ff4561e2929a2c37ce706cb8233b7907e0cdc22eab98888aca5dd3775feb", + "sha256:0d321a39c36e5f2c4ff12b4ed58d41390460f798422c4504e09eb5678e09998c", + "sha256:1e8225a1070cd8eec59a996c43229fe8f95689cb16e552d130b9793cb570a288", + "sha256:20241b3cb4f425e971cb0a8e4ffc9b0a861530ae3c52f2b0434e6c1b57e9fd95", + "sha256:25ad220258349a12ae87ede08a7b04aca51237721f63b1808d39bdb4b2164558", + "sha256:33892118b165401f291070100d6d09359ca74addda679b60390b09f8ef325ffe", + "sha256:33c6e81d7bd55b468d2e793517c909b139960b6c790a60b7991b9b6b76fb9791", + "sha256:3447475585bae2e77ecb832fc0300c3695516a47d46cefa0528181a34c5b9d3d", + "sha256:34ca2dac9e4d7afb0bed4677512e36a52f09caa6fded70b4e3e1c89dbd92c326", + "sha256:3e63498f680547ed24d2c71e6497f24bca791aca2fe116dbc2bd0ac7f191691b", + "sha256:548430be2740e327b3fe0201abe471f314741efcb0067ec4f2d7dcfb4825f3e4", + "sha256:6196a5c39286cc37c024cd78834fb9345e464525d8991c21e908cc046d1cc02c", + "sha256:61b32d06ae8a036a6607805e6720ef00a3c98207038444ba7fd3d169cd998910", + "sha256:6286efab1ed6e74b7028327365cf7346b1d777d63ab30e21a0f4d5b275fc17d5", + "sha256:65d98939f1a2e74b58839f8c4dab3b6b3c1ce84972ae712be02845e65391ac7c", + "sha256:66324e4e1beede9ac79e60f88de548da58b1f8ab4b2f1354d8375774f997e6c0", + "sha256:6c77c9937962577a6a76917845d06af6ab9197702a42e1346d8ae2e76b5e3675", + "sha256:70dec29e8ac485dbf57481baee40781c63e381bebea080991893cd297742b8fd", + "sha256:7250a3fa399f08ec9cb3f7b1b987955d17e044f1ade821b32e5f435130250d7f", + "sha256:748290bf9112b581c525e6e6d3820621ff020ed95af6f17fedef416b27ed564c", + "sha256:7da13da6f985aab7f6f28debab00c67ff9cbacd588e8477034c0652ac141feea", + "sha256:8f959b26f2634a091bb42241c3ed8d3cedb506e7c27b8dd5c7b9f745318ddbb6", + "sha256:9de9e5188a782be6b1ce866e8a51bc76a0fbaa0e16613823fc38e4fc2556ad05", + "sha256:a48900ecea1cbb71b8c71c620dee15b62f85f7c14189bdeee54966fbd9a0c5bd", + "sha256:b87936fd2c317b6ee08a5741ea06b9d11a6074ef4cc42e031bc6403f82a32575", + "sha256:c77da1263aa361938476f04c4b6c8916001b90b2c2fdd92d8d535e1af48fba5a", + "sha256:cb5ec8eead331e3bb4ce8066cf06d2dfef1bfb1b2a73082dfe8a161301b76e37", + "sha256:cc0ee35043162abbf717b7df924597ade8e5395e7b66d18270116f8745ceb795", + "sha256:d14d30e7f46a0476efb0deb5b61343b1526f73ebb5ed84f23dc794bdb88f9d9f", + "sha256:d371e811d6b156d82aa5f9a4e08b58debf97c302a35714f6f45e35139c332e32", + "sha256:d3d20ea5782ba63ed13bc2b8c291a053c8d807a8fa927d941bd718468f7b950c", + "sha256:d3f7594930c423fd9f5d1a76bee85a2c36fd8b4b16921cae7e965f22575e9c01", + "sha256:dcef026f608f678c118779cd6591c8af6e9b4155c44e0d1bc0c87c036fb8c8c4", + "sha256:e0791ac58d91ac58f694d8d2957884df8e4e2f6687cdf367ef7eb7497f79eaa2", + "sha256:e385b637ac3acaae8022e7e47dfa7b83d3620e432e3ecb9a3f7f58f150e50921", + "sha256:e519d64089b0876c7b467274468709dadf11e41d65f63bba207e04217f47c085", + "sha256:e7229e60ac41a1202444497ddde70a48d33909e484f96eb0da9baf8dc68541df", + "sha256:ed3ad863b1b40cd1d4bd21e7498329ccaece75db5a5bf58cd3c9f130843e7102", + "sha256:f0ba29bafd8e7e22920567ce0d232c26d4d47c8b5cf4ed7b562b5db39fa199c5", + "sha256:fa2ba70284fa42c2a5ecb35e322e68823288a4251f9ba9cc77be04ae15eada68", + "sha256:fba85b6cd9c39be262fcd23865652920832b61583de2a2ca907dbd8e8a8c81e5" + ], + "markers": "python_version >= '3.5'", + "version": "==6.1" + }, + "traitlets": { + "hashes": [ + "sha256:059f456c5a7c1c82b98c2e8c799f39c9b8128f6d0d46941ee118daace9eb70c7", + "sha256:2d313cc50a42cd6c277e7d7dc8d4d7fedd06a2c215f78766ae7b1a66277e0033" + ], + "markers": "python_version >= '3.7'", + "version": "==5.1.1" + }, + "typing-extensions": { + "hashes": [ + "sha256:1a9462dcc3347a79b1f1c0271fbe79e844580bb598bafa1ed208b94da3cdcd42", + "sha256:21c85e0fe4b9a155d0799430b0ad741cdce7e359660ccbd8b530613e8df88ce2" + ], + "markers": "python_version < '3.10'", + "version": "==4.1.1" + }, + "wcwidth": { + "hashes": [ + "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784", + "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83" + ], + "version": "==0.2.5" + }, + "webencodings": { + "hashes": [ + "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78", + "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923" + ], + "version": "==0.5.1" + }, + "widgetsnbextension": { + "hashes": [ + "sha256:763a9fdc836d141fa080005a886d63f66f73d56dba1fb5961afc239c77708569", + "sha256:e0731a60ba540cd19bbbefe771a9076dcd2dde90713a8f87f27f53f2d1db7727" + ], + "version": "==3.5.2" + } + }, + "develop": {} +} From 2406bb8604fa2ab4b5829c3fcffcc1847a17a618 Mon Sep 17 00:00:00 2001 From: fbaptiste <6551123+fbaptiste@users.noreply.github.com> Date: Tue, 15 Mar 2022 23:28:45 -0700 Subject: [PATCH 06/27] added Python 3.8 relevant changes to Part 1 --- Part 1/Section 10 - Extras/Untitled.ipynb | 1224 +++++++++++++++++++++ 1 file changed, 1224 insertions(+) create mode 100644 Part 1/Section 10 - Extras/Untitled.ipynb diff --git a/Part 1/Section 10 - Extras/Untitled.ipynb b/Part 1/Section 10 - Extras/Untitled.ipynb new file mode 100644 index 00000000..f94ab95a --- /dev/null +++ b/Part 1/Section 10 - Extras/Untitled.ipynb @@ -0,0 +1,1224 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "c314393b", + "metadata": {}, + "source": [ + "### Relevant Python 3.8 Changes" + ] + }, + { + "cell_type": "markdown", + "id": "cbe45497", + "metadata": {}, + "source": [ + "The release of Python 3.8 has brought some new features.\n", + "\n", + "This is a summary of the ones **relevant to this course**." + ] + }, + { + "cell_type": "markdown", + "id": "7089277d", + "metadata": {}, + "source": [ + "#### Positional Only Parameters" + ] + }, + { + "cell_type": "markdown", + "id": "61154c87", + "metadata": {}, + "source": [ + "It is now possible to define **positional-only** parameters for Python functions." + ] + }, + { + "cell_type": "markdown", + "id": "63037bda", + "metadata": {}, + "source": [ + "As we saw earlier in this course, when you define positional parameters in a function:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "13c1ba59", + "metadata": {}, + "outputs": [], + "source": [ + "def my_func(a, b):\n", + " return a + b" + ] + }, + { + "cell_type": "markdown", + "id": "a4928079", + "metadata": {}, + "source": [ + "the user is free to pass arguments either positionally:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "0bb6fea5", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "my_func(1, 2)" + ] + }, + { + "cell_type": "markdown", + "id": "be1cfa90", + "metadata": {}, + "source": [ + "or, as named arguments:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "cb04e54f", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "my_func(b=2, a=1)" + ] + }, + { + "cell_type": "markdown", + "id": "617077dd", + "metadata": {}, + "source": [ + "Some functions in Python's built-ins ared defined in such a way that certain parameters can **only** be passed positionally, for example the `print` function:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "77c6fa8a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Help on built-in function print in module builtins:\n", + "\n", + "print(...)\n", + " print(value, ..., sep=' ', end='\\n', file=sys.stdout, flush=False)\n", + " \n", + " Prints the values to a stream, or to sys.stdout by default.\n", + " Optional keyword arguments:\n", + " file: a file-like object (stream); defaults to the current sys.stdout.\n", + " sep: string inserted between values, default a space.\n", + " end: string appended after the last value, default a newline.\n", + " flush: whether to forcibly flush the stream.\n", + "\n" + ] + } + ], + "source": [ + "help(print)" + ] + }, + { + "cell_type": "markdown", + "id": "248c2581", + "metadata": {}, + "source": [ + "That `value` cannot be passed by name:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "529c5dac", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "'value' is an invalid keyword argument for print()\n" + ] + } + ], + "source": [ + "try:\n", + " print(value=\"hello\")\n", + "except TypeError as ex:\n", + " print(ex)" + ] + }, + { + "cell_type": "markdown", + "id": "f4baed67", + "metadata": {}, + "source": [ + "Instead, the parameter **must** be passed positionally:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "8c147557", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "hello\n" + ] + } + ], + "source": [ + "print(\"hello\")" + ] + }, + { + "cell_type": "markdown", + "id": "d2840374", + "metadata": {}, + "source": [ + "Until Python 3.8, it was not possible to reproduce such behavior with user-defined functions." + ] + }, + { + "cell_type": "markdown", + "id": "158674a9", + "metadata": {}, + "source": [ + "Now you can, by using the slash character(`/`). Parameters defined **before** the `/` become **positional-only** parameters:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "aeebd0f8", + "metadata": {}, + "outputs": [], + "source": [ + "def my_func(a, b, /):\n", + " return a + b" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "858727b6", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "my_func(1, 2)" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "4f73c741", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "my_func() got some positional-only arguments passed as keyword arguments: 'a, b'\n" + ] + } + ], + "source": [ + "try:\n", + " my_func(a=1, b=2)\n", + "except TypeError as ex:\n", + " print(ex)" + ] + }, + { + "cell_type": "markdown", + "id": "f5967700", + "metadata": {}, + "source": [ + "You can of course mix this along with the special parameters `*` and `**`:" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "d17254b3", + "metadata": {}, + "outputs": [], + "source": [ + "def my_func(a, b, /, *, c):\n", + " print(a + b + c)" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "b35d884e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "13\n" + ] + } + ], + "source": [ + "my_func(1, 2, c=10)" + ] + }, + { + "cell_type": "markdown", + "id": "573aca30", + "metadata": {}, + "source": [ + "#### f-string Enhancements" + ] + }, + { + "cell_type": "markdown", + "id": "89ee51f9", + "metadata": {}, + "source": [ + "Often we use f-strings to interpolate the name of a variable and it's value:" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "f79a630b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "a=hello, b=world\n" + ] + } + ], + "source": [ + "a, b = \"hello\", \"world\"\n", + "print(f\"a={a}, b={b}\")" + ] + }, + { + "cell_type": "markdown", + "id": "24747607", + "metadata": {}, + "source": [ + "Python 3.8 provides a shortcut way of doing the same thing:" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "3ae5c0c4", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "a='hello', b='world'\n" + ] + } + ], + "source": [ + "print(f\"{a=}, {b=}\")" + ] + }, + { + "cell_type": "markdown", + "id": "141558b4", + "metadata": {}, + "source": [ + "You can even use [format specifiers](https://docs.python.org/3/library/string.html#formatspec)\n", + "to further customize the output:" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "e75716ac", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "a=hello, b=world\n" + ] + } + ], + "source": [ + "print(f\"{a=:s}, {b=:s}\")" + ] + }, + { + "cell_type": "markdown", + "id": "4dcdc5d0", + "metadata": {}, + "source": [ + "Or when dealing with other types:" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "362bf6b5", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import datetime\n", + "from math import pi" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "c91b7536", + "metadata": {}, + "outputs": [], + "source": [ + "d = datetime.utcnow()\n", + "e = pi" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "545c77a2", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "d=datetime.datetime(2022, 3, 16, 5, 45, 14, 788211), e=3.141592653589793\n" + ] + } + ], + "source": [ + "print(f\"{d=}, {e=}\")" + ] + }, + { + "cell_type": "markdown", + "id": "4637ecd5", + "metadata": {}, + "source": [ + "And applying some format specifiers:" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "75c4052b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "d=2022-03-16 05:45:14, e=3.142\n" + ] + } + ], + "source": [ + "print(f\"{d=:%Y-%m-%d %H:%M:%S}, {e=:.3f}\")" + ] + }, + { + "cell_type": "markdown", + "id": "9e3783ea", + "metadata": {}, + "source": [ + "#### The `as_integer_ratio()` Method" + ] + }, + { + "cell_type": "markdown", + "id": "15ccd5ad", + "metadata": {}, + "source": [ + "The types `bool`, `int` and `Fraction` now all implement an `as_integer_ratio()` method which returns a tuple consisting of the numerator and denominator. Remember that `Decimal` and `float` already implement the same method." + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "8313fc80", + "metadata": {}, + "outputs": [], + "source": [ + "from fractions import Fraction" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "7a1ae84a", + "metadata": {}, + "outputs": [], + "source": [ + "f = Fraction(2, 3)" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "a94fc3ed", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(2, 3)" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "f.as_integer_ratio()" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "f32942ec", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(12, 1)" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a = 12\n", + "a.as_integer_ratio()" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "06980a8c", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(1, 1)" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "flag = True\n", + "flag.as_integer_ratio()" + ] + }, + { + "cell_type": "markdown", + "id": "a3091e0e", + "metadata": {}, + "source": [ + "The advantage of this is mainly for polymorphism (or duck-typing), where you can now use `as_integer_ratio` irrespective of whether the variable is a `bool`, an `int`, a `float`, a `Decimal` or a `Fraction`." + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "id": "8a362bf8", + "metadata": {}, + "outputs": [], + "source": [ + "from decimal import Decimal" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "id": "ab4a78aa", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(33, 100)" + ] + }, + "execution_count": 37, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Decimal(\"0.33\").as_integer_ratio()" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "id": "5ee29a47", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(7070651414971679, 2251799813685248)" + ] + }, + "execution_count": 38, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(3.14).as_integer_ratio()" + ] + }, + { + "cell_type": "markdown", + "id": "df1e85de", + "metadata": {}, + "source": [ + "#### The `lru_cache` decorator" + ] + }, + { + "cell_type": "markdown", + "id": "226efb47", + "metadata": {}, + "source": [ + "As we saw in this course, we can use the `lru_cache` decorator to appky an LRU cache to our functions:" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "id": "20501526", + "metadata": {}, + "outputs": [], + "source": [ + "from functools import lru_cache" + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "id": "07d699f0", + "metadata": {}, + "outputs": [], + "source": [ + "@lru_cache(maxsize=3)\n", + "def fib(n):\n", + " if n <=2 :\n", + " return 1\n", + " else:\n", + " return fib(n-1) + fib(n-2)" + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "id": "45a0cd5a", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "354224848179261915075" + ] + }, + "execution_count": 49, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "fib(100)" + ] + }, + { + "cell_type": "markdown", + "id": "fea747d4", + "metadata": {}, + "source": [ + "If we don't specify `maxsize`, it will default to `128`:" + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "id": "447be276", + "metadata": {}, + "outputs": [], + "source": [ + "@lru_cache()\n", + "def fib(n):\n", + " if n <=2 :\n", + " return 1\n", + " else:\n", + " return fib(n-1) + fib(n-2)" + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "id": "53fee4fd", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "354224848179261915075" + ] + }, + "execution_count": 51, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "fib(100)" + ] + }, + { + "cell_type": "markdown", + "id": "5cf8e33a", + "metadata": {}, + "source": [ + "The change made to this decorator in Python 3.8 allows us not to use those empty parentheses:" + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "id": "4185658b", + "metadata": {}, + "outputs": [], + "source": [ + "@lru_cache\n", + "def fib(n):\n", + " if n <=2 :\n", + " return 1\n", + " else:\n", + " return fib(n-1) + fib(n-2)" + ] + }, + { + "cell_type": "markdown", + "id": "76a9aed5", + "metadata": {}, + "source": [ + "#### `math` Module" + ] + }, + { + "cell_type": "markdown", + "id": "0af410c0", + "metadata": {}, + "source": [ + "Many examples I use throughout this course calculate the Euclidean distance between two points:" + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "id": "b511addc", + "metadata": {}, + "outputs": [], + "source": [ + "import math" + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "id": "0b66c006", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1.4142135623730951\n" + ] + } + ], + "source": [ + "a = (0, 0)\n", + "b = (1, 1)\n", + "\n", + "dist = math.sqrt((b[0] - a[1]) ** 2 + (b[1] - a[1]) ** 2)\n", + "print(dist)" + ] + }, + { + "cell_type": "markdown", + "id": "d276589d", + "metadata": {}, + "source": [ + "Now, it's much easier using the `dist()` function the `math` module:" + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "id": "1f774757", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "1.4142135623730951" + ] + }, + "execution_count": 56, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "math.dist(a, b)" + ] + }, + { + "cell_type": "markdown", + "id": "13afb1f1", + "metadata": {}, + "source": [ + "#### The `namedtuple` Implementation" + ] + }, + { + "cell_type": "markdown", + "id": "a22a839e", + "metadata": {}, + "source": [ + "Actually these changes were added to Python 3.7, but since I don't have a separate lecture for Python 3.7 changes (most did not apply to this course), here it is." + ] + }, + { + "cell_type": "markdown", + "id": "1a579fbb", + "metadata": {}, + "source": [ + "The `_source` attribute was **removed**. There quite a discussion on this, and the the core dev who implemented and supported this essentially gave up trying to keep this in - it was deemed to cause too much \"overhead\". So, sadly (wearing my teacher's hat), it is gone. It is no more. It's not pining, it's just dead. :-)" + ] + }, + { + "cell_type": "markdown", + "id": "d5e3bfd0", + "metadata": {}, + "source": [ + "The method I showed you for defining defaults for named tuples still works, and could still be used, but Python 3.7 added the `defaults` parameter to the named tuple definition." + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "id": "3538a2c2", + "metadata": {}, + "outputs": [], + "source": [ + "from collections import namedtuple" + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "id": "024bcc51", + "metadata": {}, + "outputs": [], + "source": [ + "NT = namedtuple(\"NT\", \"a b c\", defaults=(10, 20, 30))" + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "id": "9dd8a877", + "metadata": {}, + "outputs": [], + "source": [ + "nt = NT()" + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "id": "db7f47e7", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "NT(a=10, b=20, c=30)" + ] + }, + "execution_count": 60, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nt" + ] + }, + { + "cell_type": "markdown", + "id": "fb0bc811", + "metadata": {}, + "source": [ + "You don't have to specify defaults for everything, but if you do not, be aware that defaults will be applied from **right** to **left**. Which makes sense given that in Python non-defaulted parameters must be defined **before** defaulted parameters." + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "id": "8f6ad2ca", + "metadata": {}, + "outputs": [], + "source": [ + "NT = namedtuple(\"NT\", \"a b c\", defaults = (20, 30))" + ] + }, + { + "cell_type": "code", + "execution_count": 63, + "id": "bc9d5345", + "metadata": {}, + "outputs": [], + "source": [ + "nt = NT(10)" + ] + }, + { + "cell_type": "code", + "execution_count": 64, + "id": "eceeaec2", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "NT(a=10, b=20, c=30)" + ] + }, + "execution_count": 64, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nt" + ] + }, + { + "cell_type": "markdown", + "id": "64188b1c", + "metadata": {}, + "source": [ + "Note that with this way of specifying defaults you can easily define the same default for all items in the named tuple using the `*` operator:" + ] + }, + { + "cell_type": "code", + "execution_count": 66, + "id": "8b36d7c3", + "metadata": {}, + "outputs": [], + "source": [ + "NT = namedtuple(\"NT\", \"a b c d e f\", defaults=(\"xyz\",) * 6)" + ] + }, + { + "cell_type": "code", + "execution_count": 67, + "id": "1d448806", + "metadata": {}, + "outputs": [], + "source": [ + "nt = NT()" + ] + }, + { + "cell_type": "code", + "execution_count": 68, + "id": "b16758de", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "NT(a='xyz', b='xyz', c='xyz', d='xyz', e='xyz', f='xyz')" + ] + }, + "execution_count": 68, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nt" + ] + }, + { + "cell_type": "markdown", + "id": "bbcbaf53", + "metadata": {}, + "source": [ + "Just be careful if you use a **mutable** type to do this!!" + ] + }, + { + "cell_type": "code", + "execution_count": 71, + "id": "91b28470", + "metadata": {}, + "outputs": [], + "source": [ + "NT = namedtuple(\"NT\", \"a b c\", defaults = ([],) * 3)" + ] + }, + { + "cell_type": "code", + "execution_count": 72, + "id": "cad11f5c", + "metadata": {}, + "outputs": [], + "source": [ + "nt = NT()" + ] + }, + { + "cell_type": "code", + "execution_count": 73, + "id": "0cb7ceac", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "NT(a=[], b=[], c=[])" + ] + }, + "execution_count": 73, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nt" + ] + }, + { + "cell_type": "code", + "execution_count": 74, + "id": "466d9520", + "metadata": {}, + "outputs": [], + "source": [ + "nt.a.append(10)" + ] + }, + { + "cell_type": "code", + "execution_count": 75, + "id": "74826099", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[10]" + ] + }, + "execution_count": 75, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nt.a" + ] + }, + { + "cell_type": "markdown", + "id": "6d00b122", + "metadata": {}, + "source": [ + "But watch this!" + ] + }, + { + "cell_type": "code", + "execution_count": 76, + "id": "4759e6f4", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "NT(a=[10], b=[10], c=[10])" + ] + }, + "execution_count": 76, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nt" + ] + }, + { + "cell_type": "markdown", + "id": "972c666e", + "metadata": {}, + "source": [ + "I hope you understand what happened here without me telling you!" + ] + }, + { + "cell_type": "markdown", + "id": "6590d73a", + "metadata": {}, + "source": [ + "The **same** list object was re-used 3 times in the defaults." + ] + }, + { + "cell_type": "markdown", + "id": "720e13bb", + "metadata": {}, + "source": [ + "You can easily recover your defaults using the `_field_defaults` method:" + ] + }, + { + "cell_type": "code", + "execution_count": 78, + "id": "586498ae", + "metadata": {}, + "outputs": [], + "source": [ + "NT = namedtuple(\"NT\", \"a, b, c\", defaults=(1, 2, 3))" + ] + }, + { + "cell_type": "code", + "execution_count": 79, + "id": "20f4012e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'a': 1, 'b': 2, 'c': 3}" + ] + }, + "execution_count": 79, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "NT._field_defaults" + ] + }, + { + "cell_type": "markdown", + "id": "70c3a717", + "metadata": {}, + "source": [ + "One change of note in Python 3.8, the `_as_dict()` method now returns a standard dictionary (key ordered in the same way as the named tuple). Prior to this version, it would return an `OrderedDict` since standard Python dictionaries did not guarantee any specific key order, but since they now do, there's no need to use the `DefaultDict` anymore." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "65bec271", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.9.10" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From f4db2d16f0bf37054807cd29b697a262f8871465 Mon Sep 17 00:00:00 2001 From: fbaptiste <6551123+fbaptiste@users.noreply.github.com> Date: Tue, 15 Mar 2022 23:30:25 -0700 Subject: [PATCH 07/27] renamed untitled notebook --- .../{Untitled.ipynb => 14 - Relevant Python 3.8 Changes.ipynb} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Part 1/Section 10 - Extras/{Untitled.ipynb => 14 - Relevant Python 3.8 Changes.ipynb} (100%) diff --git a/Part 1/Section 10 - Extras/Untitled.ipynb b/Part 1/Section 10 - Extras/14 - Relevant Python 3.8 Changes.ipynb similarity index 100% rename from Part 1/Section 10 - Extras/Untitled.ipynb rename to Part 1/Section 10 - Extras/14 - Relevant Python 3.8 Changes.ipynb From 947da116e5eca208dbd58a655886d470d71d863a Mon Sep 17 00:00:00 2001 From: fbaptiste <6551123+fbaptiste@users.noreply.github.com> Date: Tue, 15 Mar 2022 23:32:45 -0700 Subject: [PATCH 08/27] Added link to official release notes --- .../14 - Relevant Python 3.8 Changes.ipynb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Part 1/Section 10 - Extras/14 - Relevant Python 3.8 Changes.ipynb b/Part 1/Section 10 - Extras/14 - Relevant Python 3.8 Changes.ipynb index f94ab95a..6772978f 100644 --- a/Part 1/Section 10 - Extras/14 - Relevant Python 3.8 Changes.ipynb +++ b/Part 1/Section 10 - Extras/14 - Relevant Python 3.8 Changes.ipynb @@ -15,7 +15,9 @@ "source": [ "The release of Python 3.8 has brought some new features.\n", "\n", - "This is a summary of the ones **relevant to this course**." + "This is a summary of the ones _I_ deemed relevant to this course, and does not include all the changes.\n", + "\n", + "For full release details, see [here](https://docs.python.org/3/whatsnew/3.8.html#summary-release-highlights)" ] }, { From 7cc508115e4cdf20fac913fab4732c2edf53f257 Mon Sep 17 00:00:00 2001 From: fbaptiste <6551123+fbaptiste@users.noreply.github.com> Date: Sat, 19 Mar 2022 23:04:25 -0700 Subject: [PATCH 09/27] Python updates --- .../14 - Relevant Python 3.8 Changes.ipynb | 408 ++- .../15 - Relevant Python 3.9 Changes.ipynb | 2343 +++++++++++++++++ .../16 - Relevant Python 3.10 Changes.ipynb | 991 +++++++ Pipfile | 4 +- Pipfile.lock | 309 ++- 5 files changed, 3814 insertions(+), 241 deletions(-) create mode 100644 Part 1/Section 10 - Extras/15 - Relevant Python 3.9 Changes.ipynb create mode 100644 Part 1/Section 10 - Extras/16 - Relevant Python 3.10 Changes.ipynb diff --git a/Part 1/Section 10 - Extras/14 - Relevant Python 3.8 Changes.ipynb b/Part 1/Section 10 - Extras/14 - Relevant Python 3.8 Changes.ipynb index 6772978f..699f8626 100644 --- a/Part 1/Section 10 - Extras/14 - Relevant Python 3.8 Changes.ipynb +++ b/Part 1/Section 10 - Extras/14 - Relevant Python 3.8 Changes.ipynb @@ -15,9 +15,9 @@ "source": [ "The release of Python 3.8 has brought some new features.\n", "\n", - "This is a summary of the ones _I_ deemed relevant to this course, and does not include all the changes.\n", + "This is a summary of the ones _I_ deemed relevant to this course, and does **not** include all the changes!\n", "\n", - "For full release details, see [here](https://docs.python.org/3/whatsnew/3.8.html#summary-release-highlights)" + "For full release details, see [here](https://docs.python.org/3/whatsnew/3.8.html)" ] }, { @@ -46,7 +46,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 1, "id": "13c1ba59", "metadata": {}, "outputs": [], @@ -65,7 +65,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 2, "id": "0bb6fea5", "metadata": {}, "outputs": [ @@ -75,7 +75,7 @@ "3" ] }, - "execution_count": 3, + "execution_count": 2, "metadata": {}, "output_type": "execute_result" } @@ -94,7 +94,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 3, "id": "cb04e54f", "metadata": {}, "outputs": [ @@ -104,7 +104,7 @@ "3" ] }, - "execution_count": 4, + "execution_count": 3, "metadata": {}, "output_type": "execute_result" } @@ -123,7 +123,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 4, "id": "77c6fa8a", "metadata": {}, "outputs": [ @@ -160,7 +160,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 5, "id": "529c5dac", "metadata": {}, "outputs": [ @@ -189,7 +189,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 6, "id": "8c147557", "metadata": {}, "outputs": [ @@ -223,7 +223,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 7, "id": "aeebd0f8", "metadata": {}, "outputs": [], @@ -234,7 +234,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 8, "id": "858727b6", "metadata": {}, "outputs": [ @@ -244,7 +244,7 @@ "3" ] }, - "execution_count": 11, + "execution_count": 8, "metadata": {}, "output_type": "execute_result" } @@ -255,7 +255,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 9, "id": "4f73c741", "metadata": {}, "outputs": [ @@ -284,7 +284,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 10, "id": "d17254b3", "metadata": {}, "outputs": [], @@ -295,7 +295,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 11, "id": "b35d884e", "metadata": {}, "outputs": [ @@ -329,7 +329,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 12, "id": "f79a630b", "metadata": {}, "outputs": [ @@ -356,7 +356,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 13, "id": "3ae5c0c4", "metadata": {}, "outputs": [ @@ -383,7 +383,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 14, "id": "e75716ac", "metadata": {}, "outputs": [ @@ -409,7 +409,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 15, "id": "362bf6b5", "metadata": {}, "outputs": [], @@ -420,7 +420,7 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 16, "id": "c91b7536", "metadata": {}, "outputs": [], @@ -431,7 +431,7 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 17, "id": "545c77a2", "metadata": {}, "outputs": [ @@ -439,7 +439,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "d=datetime.datetime(2022, 3, 16, 5, 45, 14, 788211), e=3.141592653589793\n" + "d=datetime.datetime(2022, 3, 20, 6, 1, 13, 990493), e=3.141592653589793\n" ] } ], @@ -457,7 +457,7 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 18, "id": "75c4052b", "metadata": {}, "outputs": [ @@ -465,7 +465,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "d=2022-03-16 05:45:14, e=3.142\n" + "d=2022-03-20 06:01:13, e=3.142\n" ] } ], @@ -473,6 +473,33 @@ "print(f\"{d=:%Y-%m-%d %H:%M:%S}, {e=:.3f}\")" ] }, + { + "cell_type": "markdown", + "id": "076773ed", + "metadata": {}, + "source": [ + "It will even display the text of an expression if you use one in your f-string:" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "0ce46ace", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1 + 2=3, ' '.join(sentence)='Python rocks!'\n" + ] + } + ], + "source": [ + "sentence = [\"Python\", \"rocks!\"]\n", + "print(f\"{1 + 2=}, {' '.join(sentence)=}\")" + ] + }, { "cell_type": "markdown", "id": "9e3783ea", @@ -491,7 +518,7 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 20, "id": "8313fc80", "metadata": {}, "outputs": [], @@ -501,7 +528,7 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 21, "id": "7a1ae84a", "metadata": {}, "outputs": [], @@ -511,7 +538,7 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 22, "id": "a94fc3ed", "metadata": {}, "outputs": [ @@ -521,7 +548,7 @@ "(2, 3)" ] }, - "execution_count": 28, + "execution_count": 22, "metadata": {}, "output_type": "execute_result" } @@ -532,7 +559,7 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 23, "id": "f32942ec", "metadata": {}, "outputs": [ @@ -542,7 +569,7 @@ "(12, 1)" ] }, - "execution_count": 29, + "execution_count": 23, "metadata": {}, "output_type": "execute_result" } @@ -554,7 +581,7 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 24, "id": "06980a8c", "metadata": {}, "outputs": [ @@ -564,7 +591,7 @@ "(1, 1)" ] }, - "execution_count": 30, + "execution_count": 24, "metadata": {}, "output_type": "execute_result" } @@ -584,7 +611,7 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 25, "id": "8a362bf8", "metadata": {}, "outputs": [], @@ -594,7 +621,7 @@ }, { "cell_type": "code", - "execution_count": 37, + "execution_count": 26, "id": "ab4a78aa", "metadata": {}, "outputs": [ @@ -604,7 +631,7 @@ "(33, 100)" ] }, - "execution_count": 37, + "execution_count": 26, "metadata": {}, "output_type": "execute_result" } @@ -615,7 +642,7 @@ }, { "cell_type": "code", - "execution_count": 38, + "execution_count": 27, "id": "5ee29a47", "metadata": {}, "outputs": [ @@ -625,7 +652,7 @@ "(7070651414971679, 2251799813685248)" ] }, - "execution_count": 38, + "execution_count": 27, "metadata": {}, "output_type": "execute_result" } @@ -652,7 +679,7 @@ }, { "cell_type": "code", - "execution_count": 40, + "execution_count": 28, "id": "20501526", "metadata": {}, "outputs": [], @@ -662,7 +689,7 @@ }, { "cell_type": "code", - "execution_count": 48, + "execution_count": 29, "id": "07d699f0", "metadata": {}, "outputs": [], @@ -677,7 +704,7 @@ }, { "cell_type": "code", - "execution_count": 49, + "execution_count": 30, "id": "45a0cd5a", "metadata": {}, "outputs": [ @@ -687,7 +714,7 @@ "354224848179261915075" ] }, - "execution_count": 49, + "execution_count": 30, "metadata": {}, "output_type": "execute_result" } @@ -706,7 +733,7 @@ }, { "cell_type": "code", - "execution_count": 50, + "execution_count": 31, "id": "447be276", "metadata": {}, "outputs": [], @@ -721,7 +748,7 @@ }, { "cell_type": "code", - "execution_count": 51, + "execution_count": 32, "id": "53fee4fd", "metadata": {}, "outputs": [ @@ -731,7 +758,7 @@ "354224848179261915075" ] }, - "execution_count": 51, + "execution_count": 32, "metadata": {}, "output_type": "execute_result" } @@ -750,7 +777,7 @@ }, { "cell_type": "code", - "execution_count": 52, + "execution_count": 33, "id": "4185658b", "metadata": {}, "outputs": [], @@ -781,7 +808,7 @@ }, { "cell_type": "code", - "execution_count": 53, + "execution_count": 34, "id": "b511addc", "metadata": {}, "outputs": [], @@ -791,7 +818,7 @@ }, { "cell_type": "code", - "execution_count": 54, + "execution_count": 35, "id": "0b66c006", "metadata": {}, "outputs": [ @@ -821,7 +848,7 @@ }, { "cell_type": "code", - "execution_count": 56, + "execution_count": 36, "id": "1f774757", "metadata": {}, "outputs": [ @@ -831,7 +858,7 @@ "1.4142135623730951" ] }, - "execution_count": 56, + "execution_count": 36, "metadata": {}, "output_type": "execute_result" } @@ -874,7 +901,7 @@ }, { "cell_type": "code", - "execution_count": 57, + "execution_count": 37, "id": "3538a2c2", "metadata": {}, "outputs": [], @@ -884,7 +911,7 @@ }, { "cell_type": "code", - "execution_count": 58, + "execution_count": 38, "id": "024bcc51", "metadata": {}, "outputs": [], @@ -894,7 +921,7 @@ }, { "cell_type": "code", - "execution_count": 59, + "execution_count": 39, "id": "9dd8a877", "metadata": {}, "outputs": [], @@ -904,7 +931,7 @@ }, { "cell_type": "code", - "execution_count": 60, + "execution_count": 40, "id": "db7f47e7", "metadata": {}, "outputs": [ @@ -914,7 +941,7 @@ "NT(a=10, b=20, c=30)" ] }, - "execution_count": 60, + "execution_count": 40, "metadata": {}, "output_type": "execute_result" } @@ -933,7 +960,7 @@ }, { "cell_type": "code", - "execution_count": 61, + "execution_count": 41, "id": "8f6ad2ca", "metadata": {}, "outputs": [], @@ -943,7 +970,7 @@ }, { "cell_type": "code", - "execution_count": 63, + "execution_count": 42, "id": "bc9d5345", "metadata": {}, "outputs": [], @@ -953,7 +980,7 @@ }, { "cell_type": "code", - "execution_count": 64, + "execution_count": 43, "id": "eceeaec2", "metadata": {}, "outputs": [ @@ -963,7 +990,7 @@ "NT(a=10, b=20, c=30)" ] }, - "execution_count": 64, + "execution_count": 43, "metadata": {}, "output_type": "execute_result" } @@ -982,7 +1009,7 @@ }, { "cell_type": "code", - "execution_count": 66, + "execution_count": 44, "id": "8b36d7c3", "metadata": {}, "outputs": [], @@ -992,7 +1019,7 @@ }, { "cell_type": "code", - "execution_count": 67, + "execution_count": 45, "id": "1d448806", "metadata": {}, "outputs": [], @@ -1002,7 +1029,7 @@ }, { "cell_type": "code", - "execution_count": 68, + "execution_count": 46, "id": "b16758de", "metadata": {}, "outputs": [ @@ -1012,7 +1039,7 @@ "NT(a='xyz', b='xyz', c='xyz', d='xyz', e='xyz', f='xyz')" ] }, - "execution_count": 68, + "execution_count": 46, "metadata": {}, "output_type": "execute_result" } @@ -1031,7 +1058,7 @@ }, { "cell_type": "code", - "execution_count": 71, + "execution_count": 47, "id": "91b28470", "metadata": {}, "outputs": [], @@ -1041,7 +1068,7 @@ }, { "cell_type": "code", - "execution_count": 72, + "execution_count": 48, "id": "cad11f5c", "metadata": {}, "outputs": [], @@ -1051,7 +1078,7 @@ }, { "cell_type": "code", - "execution_count": 73, + "execution_count": 49, "id": "0cb7ceac", "metadata": {}, "outputs": [ @@ -1061,7 +1088,7 @@ "NT(a=[], b=[], c=[])" ] }, - "execution_count": 73, + "execution_count": 49, "metadata": {}, "output_type": "execute_result" } @@ -1072,7 +1099,7 @@ }, { "cell_type": "code", - "execution_count": 74, + "execution_count": 50, "id": "466d9520", "metadata": {}, "outputs": [], @@ -1082,7 +1109,7 @@ }, { "cell_type": "code", - "execution_count": 75, + "execution_count": 51, "id": "74826099", "metadata": {}, "outputs": [ @@ -1092,7 +1119,7 @@ "[10]" ] }, - "execution_count": 75, + "execution_count": 51, "metadata": {}, "output_type": "execute_result" } @@ -1111,7 +1138,7 @@ }, { "cell_type": "code", - "execution_count": 76, + "execution_count": 52, "id": "4759e6f4", "metadata": {}, "outputs": [ @@ -1121,7 +1148,7 @@ "NT(a=[10], b=[10], c=[10])" ] }, - "execution_count": 76, + "execution_count": 52, "metadata": {}, "output_type": "execute_result" } @@ -1156,7 +1183,7 @@ }, { "cell_type": "code", - "execution_count": 78, + "execution_count": 53, "id": "586498ae", "metadata": {}, "outputs": [], @@ -1166,7 +1193,7 @@ }, { "cell_type": "code", - "execution_count": 79, + "execution_count": 54, "id": "20f4012e", "metadata": {}, "outputs": [ @@ -1176,7 +1203,7 @@ "{'a': 1, 'b': 2, 'c': 3}" ] }, - "execution_count": 79, + "execution_count": 54, "metadata": {}, "output_type": "execute_result" } @@ -1193,13 +1220,236 @@ "One change of note in Python 3.8, the `_as_dict()` method now returns a standard dictionary (key ordered in the same way as the named tuple). Prior to this version, it would return an `OrderedDict` since standard Python dictionaries did not guarantee any specific key order, but since they now do, there's no need to use the `DefaultDict` anymore." ] }, + { + "cell_type": "markdown", + "id": "ecb5b3a0", + "metadata": {}, + "source": [ + "#### Other Things" + ] + }, + { + "cell_type": "markdown", + "id": "9d8ec24b", + "metadata": {}, + "source": [ + "These are few other odds and ends that you might find of interest:" + ] + }, + { + "cell_type": "markdown", + "id": "3602bbab", + "metadata": {}, + "source": [ + "The built-in `reversed` function now works with dictionary views:" + ] + }, { "cell_type": "code", - "execution_count": null, - "id": "65bec271", + "execution_count": 55, + "id": "4f68e46b", "metadata": {}, "outputs": [], - "source": [] + "source": [ + "d = {'a': 1, 'b': 2}" + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "id": "83fc356d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['a', 'b']" + ] + }, + "execution_count": 56, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list(d.keys())" + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "id": "10648abb", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['b', 'a']" + ] + }, + "execution_count": 57, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list(reversed(d.keys()))" + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "id": "4a4885c3", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[2, 1]" + ] + }, + "execution_count": 58, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list(reversed(d.values()))" + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "id": "13785eb9", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[('b', 2), ('a', 1)]" + ] + }, + "execution_count": 59, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list(reversed(d.items()))" + ] + }, + { + "cell_type": "markdown", + "id": "e14d5c14", + "metadata": {}, + "source": [ + "The `continue` statement was not permitted in the `finally` clause of a loop, but is now supported." + ] + }, + { + "cell_type": "markdown", + "id": "fe507127", + "metadata": {}, + "source": [ + "Earlier in Part 1, we discussed string interning, as well as how a small selection of integers are essentially \"cached\" by Python and re-used whenever the literal is encountered. This meant we could use `is` instead of `==` in some cases, and that helped us get a clearer understanding of what's going on. **BUT**, as we also discussed, you should **not**, in practice, use `is` for comparing objects such as integers, strings, etc (usually we are more interested in whether is the same value, rather than the identical object) - the fact that this works is an implementation detail and not guaranteed to work the same way from one Python version to another.\n", + "\n", + "Although linters will usually catch those kinds of issues, not everyone uses a Python linter - so Python 3.8 will now emit a **warning** if you compare variables of certain types with literal constants!" + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "id": "e659956e", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "<>:2: SyntaxWarning: \"is\" with a literal. Did you mean \"==\"?\n", + "<>:2: SyntaxWarning: \"is\" with a literal. Did you mean \"==\"?\n", + "/var/folders/kg/20pf1n8j6pjg09bwxz6cgw0r0000gn/T/ipykernel_33116/3780910533.py:2: SyntaxWarning: \"is\" with a literal. Did you mean \"==\"?\n", + " a is 1\n" + ] + }, + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 60, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a = 1\n", + "a is 1" + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "id": "d970904d", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "<>:2: SyntaxWarning: \"is\" with a literal. Did you mean \"==\"?\n", + "<>:2: SyntaxWarning: \"is\" with a literal. Did you mean \"==\"?\n", + "/var/folders/kg/20pf1n8j6pjg09bwxz6cgw0r0000gn/T/ipykernel_33116/4272392880.py:2: SyntaxWarning: \"is\" with a literal. Did you mean \"==\"?\n", + " a is 'hello'\n" + ] + }, + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 61, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a = 'hello'\n", + "a is 'hello'" + ] + }, + { + "cell_type": "markdown", + "id": "01c01a49", + "metadata": {}, + "source": [ + "But we do not get a warning in a situation such as this:" + ] + }, + { + "cell_type": "code", + "execution_count": 62, + "id": "c0d535bc", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 62, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a = [1, 2, 3]\n", + "\n", + "a is [1, 2, 3]" + ] } ], "metadata": { @@ -1218,7 +1468,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.10" + "version": "3.10.2" } }, "nbformat": 4, diff --git a/Part 1/Section 10 - Extras/15 - Relevant Python 3.9 Changes.ipynb b/Part 1/Section 10 - Extras/15 - Relevant Python 3.9 Changes.ipynb new file mode 100644 index 00000000..698dadce --- /dev/null +++ b/Part 1/Section 10 - Extras/15 - Relevant Python 3.9 Changes.ipynb @@ -0,0 +1,2343 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "c45f0998", + "metadata": {}, + "source": [ + "### Relevant Python 3.9 Changes" + ] + }, + { + "cell_type": "markdown", + "id": "2e965eba", + "metadata": {}, + "source": [ + "The release of Python 3.9 has brought some new features.\n", + "\n", + "This is a summary of the ones _I_ deemed relevant to this course, and does **not** include all the changes!\n", + "\n", + "For full release details, see [here](https://docs.python.org/3/whatsnew/3.9.html)" + ] + }, + { + "cell_type": "markdown", + "id": "39255227", + "metadata": {}, + "source": [ + "#### Time Zones" + ] + }, + { + "cell_type": "markdown", + "id": "26750adb", + "metadata": {}, + "source": [ + "We don't cover 3rd party libraries in this course, but if you've worked with Python in a production environment, you will likely have come across the dreaded timezone and Daylight Savings issues that plague datetimes!" + ] + }, + { + "cell_type": "markdown", + "id": "7cdfe8a5", + "metadata": {}, + "source": [ + "Most likely you will have resorted to using the `pytz` and `python-dateutil` libraries to help with that." + ] + }, + { + "cell_type": "markdown", + "id": "bc000883", + "metadata": {}, + "source": [ + "Now, Python 3.9 is proud to introduce the `zoneinfo` module to deal with timezones properly. About time too!" + ] + }, + { + "cell_type": "markdown", + "id": "c2d652dd", + "metadata": {}, + "source": [ + "For full info on this, refer to [PEP 615](https://peps.python.org/pep-0615/).\n", + "\n", + "And the Python [docs](https://docs.python.org/3.9/library/zoneinfo.html#module-zoneinfo).\n", + "\n", + "**Windows Users**: you will likely need to add a dependency on the `tzdata` [library](https://pypi.org/project/tzdata/) for the IANA time zone database. See [this note](https://docs.python.org/3.9/library/zoneinfo.html#data-sources)\n", + "\n", + "You should also take a look at this [presentation](https://pganssle-talks.github.io/chipy-nov-2020-zoneinfo/#/) by Paul Ganssle who wrote that module - very interesting read!" + ] + }, + { + "cell_type": "markdown", + "id": "80255944", + "metadata": {}, + "source": [ + "Let's look at how we might have handled timezone and DST using `pytz` and `dateutil`, and contrast that to how we can use the new `zoneinfo` module instead." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "ec129288", + "metadata": {}, + "outputs": [], + "source": [ + "import zoneinfo\n", + "from datetime import datetime, timezone\n", + "from zoneinfo import ZoneInfo\n", + "\n", + "import dateutil\n", + "import pytz" + ] + }, + { + "cell_type": "markdown", + "id": "beadc4e2", + "metadata": {}, + "source": [ + "Let's list out all the defined time zones:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "d0058e73", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Africa/Abidjan\n", + "Africa/Accra\n", + "Africa/Addis_Ababa\n", + "Africa/Algiers\n", + "Africa/Asmara\n", + "Africa/Asmera\n", + "Africa/Bamako\n", + "Africa/Bangui\n", + "Africa/Banjul\n", + "Africa/Bissau\n", + "Africa/Blantyre\n", + "Africa/Brazzaville\n", + "Africa/Bujumbura\n", + "Africa/Cairo\n", + "Africa/Casablanca\n", + "Africa/Ceuta\n", + "Africa/Conakry\n", + "Africa/Dakar\n", + "Africa/Dar_es_Salaam\n", + "Africa/Djibouti\n", + "Africa/Douala\n", + "Africa/El_Aaiun\n", + "Africa/Freetown\n", + "Africa/Gaborone\n", + "Africa/Harare\n", + "Africa/Johannesburg\n", + "Africa/Juba\n", + "Africa/Kampala\n", + "Africa/Khartoum\n", + "Africa/Kigali\n", + "Africa/Kinshasa\n", + "Africa/Lagos\n", + "Africa/Libreville\n", + "Africa/Lome\n", + "Africa/Luanda\n", + "Africa/Lubumbashi\n", + "Africa/Lusaka\n", + "Africa/Malabo\n", + "Africa/Maputo\n", + "Africa/Maseru\n", + "Africa/Mbabane\n", + "Africa/Mogadishu\n", + "Africa/Monrovia\n", + "Africa/Nairobi\n", + "Africa/Ndjamena\n", + "Africa/Niamey\n", + "Africa/Nouakchott\n", + "Africa/Ouagadougou\n", + "Africa/Porto-Novo\n", + "Africa/Sao_Tome\n", + "Africa/Timbuktu\n", + "Africa/Tripoli\n", + "Africa/Tunis\n", + "Africa/Windhoek\n", + "America/Adak\n", + "America/Anchorage\n", + "America/Anguilla\n", + "America/Antigua\n", + "America/Araguaina\n", + "America/Argentina/Buenos_Aires\n", + "America/Argentina/Catamarca\n", + "America/Argentina/ComodRivadavia\n", + "America/Argentina/Cordoba\n", + "America/Argentina/Jujuy\n", + "America/Argentina/La_Rioja\n", + "America/Argentina/Mendoza\n", + "America/Argentina/Rio_Gallegos\n", + "America/Argentina/Salta\n", + "America/Argentina/San_Juan\n", + "America/Argentina/San_Luis\n", + "America/Argentina/Tucuman\n", + "America/Argentina/Ushuaia\n", + "America/Aruba\n", + "America/Asuncion\n", + "America/Atikokan\n", + "America/Atka\n", + "America/Bahia\n", + "America/Bahia_Banderas\n", + "America/Barbados\n", + "America/Belem\n", + "America/Belize\n", + "America/Blanc-Sablon\n", + "America/Boa_Vista\n", + "America/Bogota\n", + "America/Boise\n", + "America/Buenos_Aires\n", + "America/Cambridge_Bay\n", + "America/Campo_Grande\n", + "America/Cancun\n", + "America/Caracas\n", + "America/Catamarca\n", + "America/Cayenne\n", + "America/Cayman\n", + "America/Chicago\n", + "America/Chihuahua\n", + "America/Coral_Harbour\n", + "America/Cordoba\n", + "America/Costa_Rica\n", + "America/Creston\n", + "America/Cuiaba\n", + "America/Curacao\n", + "America/Danmarkshavn\n", + "America/Dawson\n", + "America/Dawson_Creek\n", + "America/Denver\n", + "America/Detroit\n", + "America/Dominica\n", + "America/Edmonton\n", + "America/Eirunepe\n", + "America/El_Salvador\n", + "America/Ensenada\n", + "America/Fort_Nelson\n", + "America/Fort_Wayne\n", + "America/Fortaleza\n", + "America/Glace_Bay\n", + "America/Godthab\n", + "America/Goose_Bay\n", + "America/Grand_Turk\n", + "America/Grenada\n", + "America/Guadeloupe\n", + "America/Guatemala\n", + "America/Guayaquil\n", + "America/Guyana\n", + "America/Halifax\n", + "America/Havana\n", + "America/Hermosillo\n", + "America/Indiana/Indianapolis\n", + "America/Indiana/Knox\n", + "America/Indiana/Marengo\n", + "America/Indiana/Petersburg\n", + "America/Indiana/Tell_City\n", + "America/Indiana/Vevay\n", + "America/Indiana/Vincennes\n", + "America/Indiana/Winamac\n", + "America/Indianapolis\n", + "America/Inuvik\n", + "America/Iqaluit\n", + "America/Jamaica\n", + "America/Jujuy\n", + "America/Juneau\n", + "America/Kentucky/Louisville\n", + "America/Kentucky/Monticello\n", + "America/Knox_IN\n", + "America/Kralendijk\n", + "America/La_Paz\n", + "America/Lima\n", + "America/Los_Angeles\n", + "America/Louisville\n", + "America/Lower_Princes\n", + "America/Maceio\n", + "America/Managua\n", + "America/Manaus\n", + "America/Marigot\n", + "America/Martinique\n", + "America/Matamoros\n", + "America/Mazatlan\n", + "America/Mendoza\n", + "America/Menominee\n", + "America/Merida\n", + "America/Metlakatla\n", + "America/Mexico_City\n", + "America/Miquelon\n", + "America/Moncton\n", + "America/Monterrey\n", + "America/Montevideo\n", + "America/Montreal\n", + "America/Montserrat\n", + "America/Nassau\n", + "America/New_York\n", + "America/Nipigon\n", + "America/Nome\n", + "America/Noronha\n", + "America/North_Dakota/Beulah\n", + "America/North_Dakota/Center\n", + "America/North_Dakota/New_Salem\n", + "America/Nuuk\n", + "America/Ojinaga\n", + "America/Panama\n", + "America/Pangnirtung\n", + "America/Paramaribo\n", + "America/Phoenix\n", + "America/Port-au-Prince\n", + "America/Port_of_Spain\n", + "America/Porto_Acre\n", + "America/Porto_Velho\n", + "America/Puerto_Rico\n", + "America/Punta_Arenas\n", + "America/Rainy_River\n", + "America/Rankin_Inlet\n", + "America/Recife\n", + "America/Regina\n", + "America/Resolute\n", + "America/Rio_Branco\n", + "America/Rosario\n", + "America/Santa_Isabel\n", + "America/Santarem\n", + "America/Santiago\n", + "America/Santo_Domingo\n", + "America/Sao_Paulo\n", + "America/Scoresbysund\n", + "America/Shiprock\n", + "America/Sitka\n", + "America/St_Barthelemy\n", + "America/St_Johns\n", + "America/St_Kitts\n", + "America/St_Lucia\n", + "America/St_Thomas\n", + "America/St_Vincent\n", + "America/Swift_Current\n", + "America/Tegucigalpa\n", + "America/Thule\n", + "America/Thunder_Bay\n", + "America/Tijuana\n", + "America/Toronto\n", + "America/Tortola\n", + "America/Vancouver\n", + "America/Virgin\n", + "America/Whitehorse\n", + "America/Winnipeg\n", + "America/Yakutat\n", + "America/Yellowknife\n", + "Antarctica/Casey\n", + "Antarctica/Davis\n", + "Antarctica/DumontDUrville\n", + "Antarctica/Macquarie\n", + "Antarctica/Mawson\n", + "Antarctica/McMurdo\n", + "Antarctica/Palmer\n", + "Antarctica/Rothera\n", + "Antarctica/South_Pole\n", + "Antarctica/Syowa\n", + "Antarctica/Troll\n", + "Antarctica/Vostok\n", + "Arctic/Longyearbyen\n", + "Asia/Aden\n", + "Asia/Almaty\n", + "Asia/Amman\n", + "Asia/Anadyr\n", + "Asia/Aqtau\n", + "Asia/Aqtobe\n", + "Asia/Ashgabat\n", + "Asia/Ashkhabad\n", + "Asia/Atyrau\n", + "Asia/Baghdad\n", + "Asia/Bahrain\n", + "Asia/Baku\n", + "Asia/Bangkok\n", + "Asia/Barnaul\n", + "Asia/Beirut\n", + "Asia/Bishkek\n", + "Asia/Brunei\n", + "Asia/Calcutta\n", + "Asia/Chita\n", + "Asia/Choibalsan\n", + "Asia/Chongqing\n", + "Asia/Chungking\n", + "Asia/Colombo\n", + "Asia/Dacca\n", + "Asia/Damascus\n", + "Asia/Dhaka\n", + "Asia/Dili\n", + "Asia/Dubai\n", + "Asia/Dushanbe\n", + "Asia/Famagusta\n", + "Asia/Gaza\n", + "Asia/Harbin\n", + "Asia/Hebron\n", + "Asia/Ho_Chi_Minh\n", + "Asia/Hong_Kong\n", + "Asia/Hovd\n", + "Asia/Irkutsk\n", + "Asia/Istanbul\n", + "Asia/Jakarta\n", + "Asia/Jayapura\n", + "Asia/Jerusalem\n", + "Asia/Kabul\n", + "Asia/Kamchatka\n", + "Asia/Karachi\n", + "Asia/Kashgar\n", + "Asia/Kathmandu\n", + "Asia/Katmandu\n", + "Asia/Khandyga\n", + "Asia/Kolkata\n", + "Asia/Krasnoyarsk\n", + "Asia/Kuala_Lumpur\n", + "Asia/Kuching\n", + "Asia/Kuwait\n", + "Asia/Macao\n", + "Asia/Macau\n", + "Asia/Magadan\n", + "Asia/Makassar\n", + "Asia/Manila\n", + "Asia/Muscat\n", + "Asia/Nicosia\n", + "Asia/Novokuznetsk\n", + "Asia/Novosibirsk\n", + "Asia/Omsk\n", + "Asia/Oral\n", + "Asia/Phnom_Penh\n", + "Asia/Pontianak\n", + "Asia/Pyongyang\n", + "Asia/Qatar\n", + "Asia/Qostanay\n", + "Asia/Qyzylorda\n", + "Asia/Rangoon\n", + "Asia/Riyadh\n", + "Asia/Saigon\n", + "Asia/Sakhalin\n", + "Asia/Samarkand\n", + "Asia/Seoul\n", + "Asia/Shanghai\n", + "Asia/Singapore\n", + "Asia/Srednekolymsk\n", + "Asia/Taipei\n", + "Asia/Tashkent\n", + "Asia/Tbilisi\n", + "Asia/Tehran\n", + "Asia/Tel_Aviv\n", + "Asia/Thimbu\n", + "Asia/Thimphu\n", + "Asia/Tokyo\n", + "Asia/Tomsk\n", + "Asia/Ujung_Pandang\n", + "Asia/Ulaanbaatar\n", + "Asia/Ulan_Bator\n", + "Asia/Urumqi\n", + "Asia/Ust-Nera\n", + "Asia/Vientiane\n", + "Asia/Vladivostok\n", + "Asia/Yakutsk\n", + "Asia/Yangon\n", + "Asia/Yekaterinburg\n", + "Asia/Yerevan\n", + "Atlantic/Azores\n", + "Atlantic/Bermuda\n", + "Atlantic/Canary\n", + "Atlantic/Cape_Verde\n", + "Atlantic/Faeroe\n", + "Atlantic/Faroe\n", + "Atlantic/Jan_Mayen\n", + "Atlantic/Madeira\n", + "Atlantic/Reykjavik\n", + "Atlantic/South_Georgia\n", + "Atlantic/St_Helena\n", + "Atlantic/Stanley\n", + "Australia/ACT\n", + "Australia/Adelaide\n", + "Australia/Brisbane\n", + "Australia/Broken_Hill\n", + "Australia/Canberra\n", + "Australia/Currie\n", + "Australia/Darwin\n", + "Australia/Eucla\n", + "Australia/Hobart\n", + "Australia/LHI\n", + "Australia/Lindeman\n", + "Australia/Lord_Howe\n", + "Australia/Melbourne\n", + "Australia/NSW\n", + "Australia/North\n", + "Australia/Perth\n", + "Australia/Queensland\n", + "Australia/South\n", + "Australia/Sydney\n", + "Australia/Tasmania\n", + "Australia/Victoria\n", + "Australia/West\n", + "Australia/Yancowinna\n", + "Brazil/Acre\n", + "Brazil/DeNoronha\n", + "Brazil/East\n", + "Brazil/West\n", + "CET\n", + "CST6CDT\n", + "Canada/Atlantic\n", + "Canada/Central\n", + "Canada/Eastern\n", + "Canada/Mountain\n", + "Canada/Newfoundland\n", + "Canada/Pacific\n", + "Canada/Saskatchewan\n", + "Canada/Yukon\n", + "Chile/Continental\n", + "Chile/EasterIsland\n", + "Cuba\n", + "EET\n", + "EST\n", + "EST5EDT\n", + "Egypt\n", + "Eire\n", + "Etc/GMT\n", + "Etc/GMT+0\n", + "Etc/GMT+1\n", + "Etc/GMT+10\n", + "Etc/GMT+11\n", + "Etc/GMT+12\n", + "Etc/GMT+2\n", + "Etc/GMT+3\n", + "Etc/GMT+4\n", + "Etc/GMT+5\n", + "Etc/GMT+6\n", + "Etc/GMT+7\n", + "Etc/GMT+8\n", + "Etc/GMT+9\n", + "Etc/GMT-0\n", + "Etc/GMT-1\n", + "Etc/GMT-10\n", + "Etc/GMT-11\n", + "Etc/GMT-12\n", + "Etc/GMT-13\n", + "Etc/GMT-14\n", + "Etc/GMT-2\n", + "Etc/GMT-3\n", + "Etc/GMT-4\n", + "Etc/GMT-5\n", + "Etc/GMT-6\n", + "Etc/GMT-7\n", + "Etc/GMT-8\n", + "Etc/GMT-9\n", + "Etc/GMT0\n", + "Etc/Greenwich\n", + "Etc/UCT\n", + "Etc/UTC\n", + "Etc/Universal\n", + "Etc/Zulu\n", + "Europe/Amsterdam\n", + "Europe/Andorra\n", + "Europe/Astrakhan\n", + "Europe/Athens\n", + "Europe/Belfast\n", + "Europe/Belgrade\n", + "Europe/Berlin\n", + "Europe/Bratislava\n", + "Europe/Brussels\n", + "Europe/Bucharest\n", + "Europe/Budapest\n", + "Europe/Busingen\n", + "Europe/Chisinau\n", + "Europe/Copenhagen\n", + "Europe/Dublin\n", + "Europe/Gibraltar\n", + "Europe/Guernsey\n", + "Europe/Helsinki\n", + "Europe/Isle_of_Man\n", + "Europe/Istanbul\n", + "Europe/Jersey\n", + "Europe/Kaliningrad\n", + "Europe/Kiev\n", + "Europe/Kirov\n", + "Europe/Lisbon\n", + "Europe/Ljubljana\n", + "Europe/London\n", + "Europe/Luxembourg\n", + "Europe/Madrid\n", + "Europe/Malta\n", + "Europe/Mariehamn\n", + "Europe/Minsk\n", + "Europe/Monaco\n", + "Europe/Moscow\n", + "Europe/Nicosia\n", + "Europe/Oslo\n", + "Europe/Paris\n", + "Europe/Podgorica\n", + "Europe/Prague\n", + "Europe/Riga\n", + "Europe/Rome\n", + "Europe/Samara\n", + "Europe/San_Marino\n", + "Europe/Sarajevo\n", + "Europe/Saratov\n", + "Europe/Simferopol\n", + "Europe/Skopje\n", + "Europe/Sofia\n", + "Europe/Stockholm\n", + "Europe/Tallinn\n", + "Europe/Tirane\n", + "Europe/Tiraspol\n", + "Europe/Ulyanovsk\n", + "Europe/Uzhgorod\n", + "Europe/Vaduz\n", + "Europe/Vatican\n", + "Europe/Vienna\n", + "Europe/Vilnius\n", + "Europe/Volgograd\n", + "Europe/Warsaw\n", + "Europe/Zagreb\n", + "Europe/Zaporozhye\n", + "Europe/Zurich\n", + "GB\n", + "GB-Eire\n", + "GMT\n", + "GMT+0\n", + "GMT-0\n", + "GMT0\n", + "Greenwich\n", + "HST\n", + "Hongkong\n", + "Iceland\n", + "Indian/Antananarivo\n", + "Indian/Chagos\n", + "Indian/Christmas\n", + "Indian/Cocos\n", + "Indian/Comoro\n", + "Indian/Kerguelen\n", + "Indian/Mahe\n", + "Indian/Maldives\n", + "Indian/Mauritius\n", + "Indian/Mayotte\n", + "Indian/Reunion\n", + "Iran\n", + "Israel\n", + "Jamaica\n", + "Japan\n", + "Kwajalein\n", + "Libya\n", + "MET\n", + "MST\n", + "MST7MDT\n", + "Mexico/BajaNorte\n", + "Mexico/BajaSur\n", + "Mexico/General\n", + "NZ\n", + "NZ-CHAT\n", + "Navajo\n", + "PRC\n", + "PST8PDT\n", + "Pacific/Apia\n", + "Pacific/Auckland\n", + "Pacific/Bougainville\n", + "Pacific/Chatham\n", + "Pacific/Chuuk\n", + "Pacific/Easter\n", + "Pacific/Efate\n", + "Pacific/Enderbury\n", + "Pacific/Fakaofo\n", + "Pacific/Fiji\n", + "Pacific/Funafuti\n", + "Pacific/Galapagos\n", + "Pacific/Gambier\n", + "Pacific/Guadalcanal\n", + "Pacific/Guam\n", + "Pacific/Honolulu\n", + "Pacific/Johnston\n", + "Pacific/Kanton\n", + "Pacific/Kiritimati\n", + "Pacific/Kosrae\n", + "Pacific/Kwajalein\n", + "Pacific/Majuro\n", + "Pacific/Marquesas\n", + "Pacific/Midway\n", + "Pacific/Nauru\n", + "Pacific/Niue\n", + "Pacific/Norfolk\n", + "Pacific/Noumea\n", + "Pacific/Pago_Pago\n", + "Pacific/Palau\n", + "Pacific/Pitcairn\n", + "Pacific/Pohnpei\n", + "Pacific/Ponape\n", + "Pacific/Port_Moresby\n", + "Pacific/Rarotonga\n", + "Pacific/Saipan\n", + "Pacific/Samoa\n", + "Pacific/Tahiti\n", + "Pacific/Tarawa\n", + "Pacific/Tongatapu\n", + "Pacific/Truk\n", + "Pacific/Wake\n", + "Pacific/Wallis\n", + "Pacific/Yap\n", + "Poland\n", + "Portugal\n", + "ROC\n", + "ROK\n", + "Singapore\n", + "Turkey\n", + "UCT\n", + "US/Alaska\n", + "US/Aleutian\n", + "US/Arizona\n", + "US/Central\n", + "US/East-Indiana\n", + "US/Eastern\n", + "US/Hawaii\n", + "US/Indiana-Starke\n", + "US/Michigan\n", + "US/Mountain\n", + "US/Pacific\n", + "US/Samoa\n", + "UTC\n", + "Universal\n", + "W-SU\n", + "WET\n", + "Zulu\n" + ] + } + ], + "source": [ + "for tz in pytz.all_timezones:\n", + " print(tz)" + ] + }, + { + "cell_type": "markdown", + "id": "f18fa2c9", + "metadata": {}, + "source": [ + "With the `zoneinfo` module:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "9d2cf9d1", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Africa/Abidjan\n", + "Africa/Accra\n", + "Africa/Addis_Ababa\n", + "Africa/Algiers\n", + "Africa/Asmara\n", + "Africa/Asmera\n", + "Africa/Bamako\n", + "Africa/Bangui\n", + "Africa/Banjul\n", + "Africa/Bissau\n", + "Africa/Blantyre\n", + "Africa/Brazzaville\n", + "Africa/Bujumbura\n", + "Africa/Cairo\n", + "Africa/Casablanca\n", + "Africa/Ceuta\n", + "Africa/Conakry\n", + "Africa/Dakar\n", + "Africa/Dar_es_Salaam\n", + "Africa/Djibouti\n", + "Africa/Douala\n", + "Africa/El_Aaiun\n", + "Africa/Freetown\n", + "Africa/Gaborone\n", + "Africa/Harare\n", + "Africa/Johannesburg\n", + "Africa/Juba\n", + "Africa/Kampala\n", + "Africa/Khartoum\n", + "Africa/Kigali\n", + "Africa/Kinshasa\n", + "Africa/Lagos\n", + "Africa/Libreville\n", + "Africa/Lome\n", + "Africa/Luanda\n", + "Africa/Lubumbashi\n", + "Africa/Lusaka\n", + "Africa/Malabo\n", + "Africa/Maputo\n", + "Africa/Maseru\n", + "Africa/Mbabane\n", + "Africa/Mogadishu\n", + "Africa/Monrovia\n", + "Africa/Nairobi\n", + "Africa/Ndjamena\n", + "Africa/Niamey\n", + "Africa/Nouakchott\n", + "Africa/Ouagadougou\n", + "Africa/Porto-Novo\n", + "Africa/Sao_Tome\n", + "Africa/Timbuktu\n", + "Africa/Tripoli\n", + "Africa/Tunis\n", + "Africa/Windhoek\n", + "America/Adak\n", + "America/Anchorage\n", + "America/Anguilla\n", + "America/Antigua\n", + "America/Araguaina\n", + "America/Argentina/Buenos_Aires\n", + "America/Argentina/Catamarca\n", + "America/Argentina/ComodRivadavia\n", + "America/Argentina/Cordoba\n", + "America/Argentina/Jujuy\n", + "America/Argentina/La_Rioja\n", + "America/Argentina/Mendoza\n", + "America/Argentina/Rio_Gallegos\n", + "America/Argentina/Salta\n", + "America/Argentina/San_Juan\n", + "America/Argentina/San_Luis\n", + "America/Argentina/Tucuman\n", + "America/Argentina/Ushuaia\n", + "America/Aruba\n", + "America/Asuncion\n", + "America/Atikokan\n", + "America/Atka\n", + "America/Bahia\n", + "America/Bahia_Banderas\n", + "America/Barbados\n", + "America/Belem\n", + "America/Belize\n", + "America/Blanc-Sablon\n", + "America/Boa_Vista\n", + "America/Bogota\n", + "America/Boise\n", + "America/Buenos_Aires\n", + "America/Cambridge_Bay\n", + "America/Campo_Grande\n", + "America/Cancun\n", + "America/Caracas\n", + "America/Catamarca\n", + "America/Cayenne\n", + "America/Cayman\n", + "America/Chicago\n", + "America/Chihuahua\n", + "America/Coral_Harbour\n", + "America/Cordoba\n", + "America/Costa_Rica\n", + "America/Creston\n", + "America/Cuiaba\n", + "America/Curacao\n", + "America/Danmarkshavn\n", + "America/Dawson\n", + "America/Dawson_Creek\n", + "America/Denver\n", + "America/Detroit\n", + "America/Dominica\n", + "America/Edmonton\n", + "America/Eirunepe\n", + "America/El_Salvador\n", + "America/Ensenada\n", + "America/Fort_Nelson\n", + "America/Fort_Wayne\n", + "America/Fortaleza\n", + "America/Glace_Bay\n", + "America/Godthab\n", + "America/Goose_Bay\n", + "America/Grand_Turk\n", + "America/Grenada\n", + "America/Guadeloupe\n", + "America/Guatemala\n", + "America/Guayaquil\n", + "America/Guyana\n", + "America/Halifax\n", + "America/Havana\n", + "America/Hermosillo\n", + "America/Indiana/Indianapolis\n", + "America/Indiana/Knox\n", + "America/Indiana/Marengo\n", + "America/Indiana/Petersburg\n", + "America/Indiana/Tell_City\n", + "America/Indiana/Vevay\n", + "America/Indiana/Vincennes\n", + "America/Indiana/Winamac\n", + "America/Indianapolis\n", + "America/Inuvik\n", + "America/Iqaluit\n", + "America/Jamaica\n", + "America/Jujuy\n", + "America/Juneau\n", + "America/Kentucky/Louisville\n", + "America/Kentucky/Monticello\n", + "America/Knox_IN\n", + "America/Kralendijk\n", + "America/La_Paz\n", + "America/Lima\n", + "America/Los_Angeles\n", + "America/Louisville\n", + "America/Lower_Princes\n", + "America/Maceio\n", + "America/Managua\n", + "America/Manaus\n", + "America/Marigot\n", + "America/Martinique\n", + "America/Matamoros\n", + "America/Mazatlan\n", + "America/Mendoza\n", + "America/Menominee\n", + "America/Merida\n", + "America/Metlakatla\n", + "America/Mexico_City\n", + "America/Miquelon\n", + "America/Moncton\n", + "America/Monterrey\n", + "America/Montevideo\n", + "America/Montreal\n", + "America/Montserrat\n", + "America/Nassau\n", + "America/New_York\n", + "America/Nipigon\n", + "America/Nome\n", + "America/Noronha\n", + "America/North_Dakota/Beulah\n", + "America/North_Dakota/Center\n", + "America/North_Dakota/New_Salem\n", + "America/Nuuk\n", + "America/Ojinaga\n", + "America/Panama\n", + "America/Pangnirtung\n", + "America/Paramaribo\n", + "America/Phoenix\n", + "America/Port-au-Prince\n", + "America/Port_of_Spain\n", + "America/Porto_Acre\n", + "America/Porto_Velho\n", + "America/Puerto_Rico\n", + "America/Punta_Arenas\n", + "America/Rainy_River\n", + "America/Rankin_Inlet\n", + "America/Recife\n", + "America/Regina\n", + "America/Resolute\n", + "America/Rio_Branco\n", + "America/Rosario\n", + "America/Santa_Isabel\n", + "America/Santarem\n", + "America/Santiago\n", + "America/Santo_Domingo\n", + "America/Sao_Paulo\n", + "America/Scoresbysund\n", + "America/Shiprock\n", + "America/Sitka\n", + "America/St_Barthelemy\n", + "America/St_Johns\n", + "America/St_Kitts\n", + "America/St_Lucia\n", + "America/St_Thomas\n", + "America/St_Vincent\n", + "America/Swift_Current\n", + "America/Tegucigalpa\n", + "America/Thule\n", + "America/Thunder_Bay\n", + "America/Tijuana\n", + "America/Toronto\n", + "America/Tortola\n", + "America/Vancouver\n", + "America/Virgin\n", + "America/Whitehorse\n", + "America/Winnipeg\n", + "America/Yakutat\n", + "America/Yellowknife\n", + "Antarctica/Casey\n", + "Antarctica/Davis\n", + "Antarctica/DumontDUrville\n", + "Antarctica/Macquarie\n", + "Antarctica/Mawson\n", + "Antarctica/McMurdo\n", + "Antarctica/Palmer\n", + "Antarctica/Rothera\n", + "Antarctica/South_Pole\n", + "Antarctica/Syowa\n", + "Antarctica/Troll\n", + "Antarctica/Vostok\n", + "Arctic/Longyearbyen\n", + "Asia/Aden\n", + "Asia/Almaty\n", + "Asia/Amman\n", + "Asia/Anadyr\n", + "Asia/Aqtau\n", + "Asia/Aqtobe\n", + "Asia/Ashgabat\n", + "Asia/Ashkhabad\n", + "Asia/Atyrau\n", + "Asia/Baghdad\n", + "Asia/Bahrain\n", + "Asia/Baku\n", + "Asia/Bangkok\n", + "Asia/Barnaul\n", + "Asia/Beirut\n", + "Asia/Bishkek\n", + "Asia/Brunei\n", + "Asia/Calcutta\n", + "Asia/Chita\n", + "Asia/Choibalsan\n", + "Asia/Chongqing\n", + "Asia/Chungking\n", + "Asia/Colombo\n", + "Asia/Dacca\n", + "Asia/Damascus\n", + "Asia/Dhaka\n", + "Asia/Dili\n", + "Asia/Dubai\n", + "Asia/Dushanbe\n", + "Asia/Famagusta\n", + "Asia/Gaza\n", + "Asia/Harbin\n", + "Asia/Hebron\n", + "Asia/Ho_Chi_Minh\n", + "Asia/Hong_Kong\n", + "Asia/Hovd\n", + "Asia/Irkutsk\n", + "Asia/Istanbul\n", + "Asia/Jakarta\n", + "Asia/Jayapura\n", + "Asia/Jerusalem\n", + "Asia/Kabul\n", + "Asia/Kamchatka\n", + "Asia/Karachi\n", + "Asia/Kashgar\n", + "Asia/Kathmandu\n", + "Asia/Katmandu\n", + "Asia/Khandyga\n", + "Asia/Kolkata\n", + "Asia/Krasnoyarsk\n", + "Asia/Kuala_Lumpur\n", + "Asia/Kuching\n", + "Asia/Kuwait\n", + "Asia/Macao\n", + "Asia/Macau\n", + "Asia/Magadan\n", + "Asia/Makassar\n", + "Asia/Manila\n", + "Asia/Muscat\n", + "Asia/Nicosia\n", + "Asia/Novokuznetsk\n", + "Asia/Novosibirsk\n", + "Asia/Omsk\n", + "Asia/Oral\n", + "Asia/Phnom_Penh\n", + "Asia/Pontianak\n", + "Asia/Pyongyang\n", + "Asia/Qatar\n", + "Asia/Qostanay\n", + "Asia/Qyzylorda\n", + "Asia/Rangoon\n", + "Asia/Riyadh\n", + "Asia/Saigon\n", + "Asia/Sakhalin\n", + "Asia/Samarkand\n", + "Asia/Seoul\n", + "Asia/Shanghai\n", + "Asia/Singapore\n", + "Asia/Srednekolymsk\n", + "Asia/Taipei\n", + "Asia/Tashkent\n", + "Asia/Tbilisi\n", + "Asia/Tehran\n", + "Asia/Tel_Aviv\n", + "Asia/Thimbu\n", + "Asia/Thimphu\n", + "Asia/Tokyo\n", + "Asia/Tomsk\n", + "Asia/Ujung_Pandang\n", + "Asia/Ulaanbaatar\n", + "Asia/Ulan_Bator\n", + "Asia/Urumqi\n", + "Asia/Ust-Nera\n", + "Asia/Vientiane\n", + "Asia/Vladivostok\n", + "Asia/Yakutsk\n", + "Asia/Yangon\n", + "Asia/Yekaterinburg\n", + "Asia/Yerevan\n", + "Atlantic/Azores\n", + "Atlantic/Bermuda\n", + "Atlantic/Canary\n", + "Atlantic/Cape_Verde\n", + "Atlantic/Faeroe\n", + "Atlantic/Faroe\n", + "Atlantic/Jan_Mayen\n", + "Atlantic/Madeira\n", + "Atlantic/Reykjavik\n", + "Atlantic/South_Georgia\n", + "Atlantic/St_Helena\n", + "Atlantic/Stanley\n", + "Australia/ACT\n", + "Australia/Adelaide\n", + "Australia/Brisbane\n", + "Australia/Broken_Hill\n", + "Australia/Canberra\n", + "Australia/Currie\n", + "Australia/Darwin\n", + "Australia/Eucla\n", + "Australia/Hobart\n", + "Australia/LHI\n", + "Australia/Lindeman\n", + "Australia/Lord_Howe\n", + "Australia/Melbourne\n", + "Australia/NSW\n", + "Australia/North\n", + "Australia/Perth\n", + "Australia/Queensland\n", + "Australia/South\n", + "Australia/Sydney\n", + "Australia/Tasmania\n", + "Australia/Victoria\n", + "Australia/West\n", + "Australia/Yancowinna\n", + "Brazil/Acre\n", + "Brazil/DeNoronha\n", + "Brazil/East\n", + "Brazil/West\n", + "CET\n", + "CST6CDT\n", + "Canada/Atlantic\n", + "Canada/Central\n", + "Canada/Eastern\n", + "Canada/Mountain\n", + "Canada/Newfoundland\n", + "Canada/Pacific\n", + "Canada/Saskatchewan\n", + "Canada/Yukon\n", + "Chile/Continental\n", + "Chile/EasterIsland\n", + "Cuba\n", + "EET\n", + "EST\n", + "EST5EDT\n", + "Egypt\n", + "Eire\n", + "Etc/GMT\n", + "Etc/GMT+0\n", + "Etc/GMT+1\n", + "Etc/GMT+10\n", + "Etc/GMT+11\n", + "Etc/GMT+12\n", + "Etc/GMT+2\n", + "Etc/GMT+3\n", + "Etc/GMT+4\n", + "Etc/GMT+5\n", + "Etc/GMT+6\n", + "Etc/GMT+7\n", + "Etc/GMT+8\n", + "Etc/GMT+9\n", + "Etc/GMT-0\n", + "Etc/GMT-1\n", + "Etc/GMT-10\n", + "Etc/GMT-11\n", + "Etc/GMT-12\n", + "Etc/GMT-13\n", + "Etc/GMT-14\n", + "Etc/GMT-2\n", + "Etc/GMT-3\n", + "Etc/GMT-4\n", + "Etc/GMT-5\n", + "Etc/GMT-6\n", + "Etc/GMT-7\n", + "Etc/GMT-8\n", + "Etc/GMT-9\n", + "Etc/GMT0\n", + "Etc/Greenwich\n", + "Etc/UCT\n", + "Etc/UTC\n", + "Etc/Universal\n", + "Etc/Zulu\n", + "Europe/Amsterdam\n", + "Europe/Andorra\n", + "Europe/Astrakhan\n", + "Europe/Athens\n", + "Europe/Belfast\n", + "Europe/Belgrade\n", + "Europe/Berlin\n", + "Europe/Bratislava\n", + "Europe/Brussels\n", + "Europe/Bucharest\n", + "Europe/Budapest\n", + "Europe/Busingen\n", + "Europe/Chisinau\n", + "Europe/Copenhagen\n", + "Europe/Dublin\n", + "Europe/Gibraltar\n", + "Europe/Guernsey\n", + "Europe/Helsinki\n", + "Europe/Isle_of_Man\n", + "Europe/Istanbul\n", + "Europe/Jersey\n", + "Europe/Kaliningrad\n", + "Europe/Kiev\n", + "Europe/Kirov\n", + "Europe/Lisbon\n", + "Europe/Ljubljana\n", + "Europe/London\n", + "Europe/Luxembourg\n", + "Europe/Madrid\n", + "Europe/Malta\n", + "Europe/Mariehamn\n", + "Europe/Minsk\n", + "Europe/Monaco\n", + "Europe/Moscow\n", + "Europe/Nicosia\n", + "Europe/Oslo\n", + "Europe/Paris\n", + "Europe/Podgorica\n", + "Europe/Prague\n", + "Europe/Riga\n", + "Europe/Rome\n", + "Europe/Samara\n", + "Europe/San_Marino\n", + "Europe/Sarajevo\n", + "Europe/Saratov\n", + "Europe/Simferopol\n", + "Europe/Skopje\n", + "Europe/Sofia\n", + "Europe/Stockholm\n", + "Europe/Tallinn\n", + "Europe/Tirane\n", + "Europe/Tiraspol\n", + "Europe/Ulyanovsk\n", + "Europe/Uzhgorod\n", + "Europe/Vaduz\n", + "Europe/Vatican\n", + "Europe/Vienna\n", + "Europe/Vilnius\n", + "Europe/Volgograd\n", + "Europe/Warsaw\n", + "Europe/Zagreb\n", + "Europe/Zaporozhye\n", + "Europe/Zurich\n", + "Factory\n", + "GB\n", + "GB-Eire\n", + "GMT\n", + "GMT+0\n", + "GMT-0\n", + "GMT0\n", + "Greenwich\n", + "HST\n", + "Hongkong\n", + "Iceland\n", + "Indian/Antananarivo\n", + "Indian/Chagos\n", + "Indian/Christmas\n", + "Indian/Cocos\n", + "Indian/Comoro\n", + "Indian/Kerguelen\n", + "Indian/Mahe\n", + "Indian/Maldives\n", + "Indian/Mauritius\n", + "Indian/Mayotte\n", + "Indian/Reunion\n", + "Iran\n", + "Israel\n", + "Jamaica\n", + "Japan\n", + "Kwajalein\n", + "Libya\n", + "MET\n", + "MST\n", + "MST7MDT\n", + "Mexico/BajaNorte\n", + "Mexico/BajaSur\n", + "Mexico/General\n", + "NZ\n", + "NZ-CHAT\n", + "Navajo\n", + "PRC\n", + "PST8PDT\n", + "Pacific/Apia\n", + "Pacific/Auckland\n", + "Pacific/Bougainville\n", + "Pacific/Chatham\n", + "Pacific/Chuuk\n", + "Pacific/Easter\n", + "Pacific/Efate\n", + "Pacific/Enderbury\n", + "Pacific/Fakaofo\n", + "Pacific/Fiji\n", + "Pacific/Funafuti\n", + "Pacific/Galapagos\n", + "Pacific/Gambier\n", + "Pacific/Guadalcanal\n", + "Pacific/Guam\n", + "Pacific/Honolulu\n", + "Pacific/Johnston\n", + "Pacific/Kanton\n", + "Pacific/Kiritimati\n", + "Pacific/Kosrae\n", + "Pacific/Kwajalein\n", + "Pacific/Majuro\n", + "Pacific/Marquesas\n", + "Pacific/Midway\n", + "Pacific/Nauru\n", + "Pacific/Niue\n", + "Pacific/Norfolk\n", + "Pacific/Noumea\n", + "Pacific/Pago_Pago\n", + "Pacific/Palau\n", + "Pacific/Pitcairn\n", + "Pacific/Pohnpei\n", + "Pacific/Ponape\n", + "Pacific/Port_Moresby\n", + "Pacific/Rarotonga\n", + "Pacific/Saipan\n", + "Pacific/Samoa\n", + "Pacific/Tahiti\n", + "Pacific/Tarawa\n", + "Pacific/Tongatapu\n", + "Pacific/Truk\n", + "Pacific/Wake\n", + "Pacific/Wallis\n", + "Pacific/Yap\n", + "Poland\n", + "Portugal\n", + "ROC\n", + "ROK\n", + "Singapore\n", + "Turkey\n", + "UCT\n", + "US/Alaska\n", + "US/Aleutian\n", + "US/Arizona\n", + "US/Central\n", + "US/East-Indiana\n", + "US/Eastern\n", + "US/Hawaii\n", + "US/Indiana-Starke\n", + "US/Michigan\n", + "US/Mountain\n", + "US/Pacific\n", + "US/Samoa\n", + "UTC\n", + "Universal\n", + "W-SU\n", + "WET\n", + "Zulu\n" + ] + } + ], + "source": [ + "for tz in sorted(zoneinfo.available_timezones()):\n", + " print(tz)" + ] + }, + { + "cell_type": "markdown", + "id": "6a1887cf", + "metadata": {}, + "source": [ + "Are the time zones defined by `pytz` and `zoneinfo` the same? Yes!" + ] + }, + { + "cell_type": "markdown", + "id": "8f8df276", + "metadata": {}, + "source": [ + "In this example, let's take our current time in UTC, and convert it to some other time zone, say `Australia/Melbourne`." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "77f1b8d9", + "metadata": {}, + "outputs": [], + "source": [ + "now_utc_naive = datetime.utcnow()" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "f8586215", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "datetime.datetime(2022, 3, 20, 6, 1, 3, 368403)" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "now_utc_naive" + ] + }, + { + "cell_type": "markdown", + "id": "a452220a", + "metadata": {}, + "source": [ + "The problem here is that we have a _naive_ datetime (i.e. one without an attached timezone)." + ] + }, + { + "cell_type": "markdown", + "id": "d1d10809", + "metadata": {}, + "source": [ + "We can make this naive datetime time zone aware by tacking on the timezone (since we know it is UTC):" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "db60736b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "datetime.datetime(2022, 3, 20, 6, 1, 3, 368403, tzinfo=datetime.timezone.utc)" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "now_utc_aware = now_utc_naive.replace(tzinfo=timezone.utc)\n", + "now_utc_aware" + ] + }, + { + "cell_type": "markdown", + "id": "8282b097", + "metadata": {}, + "source": [ + "Or, we could use the `pytz` library to do the same thing:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "0d7434f7", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "datetime.datetime(2022, 3, 20, 6, 1, 3, 377626, tzinfo=)" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "pytz.utc.localize(datetime.utcnow())" + ] + }, + { + "cell_type": "markdown", + "id": "e613c2e1", + "metadata": {}, + "source": [ + "Now that we have a time zone aware datetime, we can convert it to another timezone using `pytz`:" + ] + }, + { + "cell_type": "markdown", + "id": "94b0b5c9", + "metadata": {}, + "source": [ + "First, let's pick a time zone from `pytz`:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "833b5ff9", + "metadata": {}, + "outputs": [], + "source": [ + "tz_melbourne = pytz.timezone('Australia/Melbourne')" + ] + }, + { + "cell_type": "markdown", + "id": "ce4323c1", + "metadata": {}, + "source": [ + "And now we localize our aware datetime to this time zone:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "d23fd3c4", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "datetime.datetime(2022, 3, 20, 17, 1, 3, 368403, tzinfo=)" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "now_utc_aware.astimezone(tz_melbourne)" + ] + }, + { + "cell_type": "markdown", + "id": "260bd7ff", + "metadata": {}, + "source": [ + "We could do both these steps in a single expression:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "6559daf7", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "datetime.datetime(2022, 3, 20, 17, 1, 3, 368403, tzinfo=)" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "now_utc_aware.astimezone(pytz.timezone('Australia/Melbourne'))" + ] + }, + { + "cell_type": "markdown", + "id": "6be99718", + "metadata": {}, + "source": [ + "Now, let's do the same thing using the `zoneinfo` module." + ] + }, + { + "cell_type": "markdown", + "id": "8c9b22bb", + "metadata": {}, + "source": [ + "Let's pick the same target time zone:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "30c58db3", + "metadata": {}, + "outputs": [], + "source": [ + "tz_zi_dublin = ZoneInfo(\"Europe/Dublin\")" + ] + }, + { + "cell_type": "markdown", + "id": "0f77b5b0", + "metadata": {}, + "source": [ + "And the let's convert our aware datetime to that time zone:" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "1623322d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "datetime.datetime(2022, 3, 20, 6, 1, 3, 368403, tzinfo=zoneinfo.ZoneInfo(key='Europe/Dublin'))" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "now_utc_aware.astimezone(tz_zi_dublin)" + ] + }, + { + "cell_type": "markdown", + "id": "4536470f", + "metadata": {}, + "source": [ + "Or, we can also write this as a single expression:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "337c3955", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "datetime.datetime(2022, 3, 20, 6, 1, 3, 368403, tzinfo=zoneinfo.ZoneInfo(key='Europe/Dublin'))" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "now_utc_aware.astimezone(ZoneInfo(\"Europe/Dublin\"))" + ] + }, + { + "cell_type": "markdown", + "id": "4076195d", + "metadata": {}, + "source": [ + "#### The `math` Module" + ] + }, + { + "cell_type": "markdown", + "id": "41d86e28", + "metadata": {}, + "source": [ + "Several enhancements or additions have been to the math library." + ] + }, + { + "cell_type": "markdown", + "id": "bd3de95f", + "metadata": {}, + "source": [ + "The `math` module already had the `gcd` function to calculate the great common divisor of two numbers:" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "911df27e", + "metadata": {}, + "outputs": [], + "source": [ + "import math" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "4154629e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "9" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "math.gcd(27, 45)" + ] + }, + { + "cell_type": "markdown", + "id": "85abe1fa", + "metadata": {}, + "source": [ + "But now `gcd` can take multiple arguments, not just two:" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "d64ea0d9", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "math.gcd(27, 45, 18, 15)" + ] + }, + { + "cell_type": "markdown", + "id": "0503d78a", + "metadata": {}, + "source": [ + "The `lcm` (least common multiple) function has been added:" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "b4c25fef", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "12" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "math.lcm(2, 3, 4)" + ] + }, + { + "cell_type": "markdown", + "id": "58a4c3cd", + "metadata": {}, + "source": [ + "#### Dictionary Unions" + ] + }, + { + "cell_type": "markdown", + "id": "2c05f130", + "metadata": {}, + "source": [ + "When we discussed dictionaries in this course, we saw that we could combine two dictionaries using unpacking:" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "136c8836", + "metadata": {}, + "outputs": [], + "source": [ + "d1 = {'a': 1, 'b': 2, 'c': 3}\n", + "d2 = {'c': 30, 'd': 40}" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "81331802", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'a': 1, 'b': 2, 'c': 30, 'd': 40}" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "{**d1, **d2}" + ] + }, + { + "cell_type": "markdown", + "id": "97ca030f", + "metadata": {}, + "source": [ + "As we saw the second dictionary's key/value pair \"overwrote\" the key/value pair from the first dictionary." + ] + }, + { + "cell_type": "markdown", + "id": "b361771e", + "metadata": {}, + "source": [ + "We could also use the `ChainMap` function in the `collections` module:" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "d633f16c", + "metadata": {}, + "outputs": [], + "source": [ + "from collections import ChainMap" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "ad250dfd", + "metadata": {}, + "outputs": [], + "source": [ + "merged = ChainMap(d1, d2)" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "1deeb8f2", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(1, 3, 40)" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "merged['a'], merged['c'], merged['d']" + ] + }, + { + "cell_type": "markdown", + "id": "1751d47a", + "metadata": {}, + "source": [ + "As you can see, in the `ChainMap`, the firest occurrence of the key is used - so in this case `c` comes from `d1`, not `d2`." + ] + }, + { + "cell_type": "markdown", + "id": "1c63e893", + "metadata": {}, + "source": [ + "Both of these ways of \"combining\" dictionaries work well - but they are not very intuitive, and need a little attention to what happens when you have common keys in the dictionaries." + ] + }, + { + "cell_type": "markdown", + "id": "aca5787b", + "metadata": {}, + "source": [ + "Think of concatenating lists where we can simply use the `+` operator - this is very intuitive:" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "de850747", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[1, 2, 3, 4, 5, 6]" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "[1, 2, 3] + [4, 5, 6]" + ] + }, + { + "cell_type": "markdown", + "id": "69429bab", + "metadata": {}, + "source": [ + "Now dictionaries are not like lists, but they are closely related to **sets**. With sets, we have the **union** operator (`|`):" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "dcb8a7b5", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'a', 'b', 'c', 'd'}" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "s1 = {'a', 'b', 'c'}\n", + "s2 = {'c', 'd'}\n", + "\n", + "s1 | s2" + ] + }, + { + "cell_type": "markdown", + "id": "32c20dfb", + "metadata": {}, + "source": [ + "Python 3.9 introduces support for the **union** (`|`) operation between dictionaries as well." + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "af3fcc01", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'a': 1, 'b': 2, 'c': 30, 'd': 40}" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "d1 | d2" + ] + }, + { + "cell_type": "markdown", + "id": "851ffadc", + "metadata": {}, + "source": [ + "Just like with the `{**d1, **d2}` approach, the value for `c` came from the second dictionary." + ] + }, + { + "cell_type": "markdown", + "id": "cb6fc060", + "metadata": {}, + "source": [ + "And just like with that technique we can control this by switching the order of the dictionaries in the union:" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "05555100", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'c': 3, 'd': 40, 'a': 1, 'b': 2}" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "d2 | d1" + ] + }, + { + "cell_type": "markdown", + "id": "739e210f", + "metadata": {}, + "source": [ + "One question we should have, is what happens to the insertion order that Python dictionaries now guarantee?" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "f8759da5", + "metadata": {}, + "outputs": [], + "source": [ + "d1 = {'c': 3, 'a': 1, 'b': 2}\n", + "d2 = {'d': 40, 'c': 30}" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "e06e3966", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'c': 30, 'a': 1, 'b': 2, 'd': 40}" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "d1 | d2" + ] + }, + { + "cell_type": "markdown", + "id": "8e0e1c2e", + "metadata": {}, + "source": [ + "As you can see, even though the **value** for `c` came from the **second** dictionary, the original inertion order of the **keys** is maintained, so `c` is still in first position in the union of the two dictionaries." + ] + }, + { + "cell_type": "markdown", + "id": "e12dee6a", + "metadata": {}, + "source": [ + "#### String Methods" + ] + }, + { + "cell_type": "markdown", + "id": "f69f79e4", + "metadata": {}, + "source": [ + "Often we need to remove some prefix or suffix in a string." + ] + }, + { + "cell_type": "markdown", + "id": "67496793", + "metadata": {}, + "source": [ + "For example, we may have this list of string:" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "9da4cabe", + "metadata": {}, + "outputs": [], + "source": [ + "data = [\n", + " \"(log) [2022-03-01T13:30:01] Log record 1\",\n", + " \"(log) [2022-03-01T13:30:02] Log record 2\",\n", + " \"(log) [2022-03-01T13:30:03] Log record 3\",\n", + " \"(log) [2022-03-01T13:30:04] Log record 4\",\n", + "]" + ] + }, + { + "cell_type": "markdown", + "id": "30de1de8", + "metadata": {}, + "source": [ + "And we want to clean these up and remove the `(log) ` prefix (including the space)." + ] + }, + { + "cell_type": "markdown", + "id": "84a6c502", + "metadata": {}, + "source": [ + "We can certainly do it this way:" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "8a60ecdc", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['[2022-03-01T13:30:01] Log record 1',\n", + " '[2022-03-01T13:30:02] Log record 2',\n", + " '[2022-03-01T13:30:03] Log record 3',\n", + " '[2022-03-01T13:30:04] Log record 4']" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "clean = [\n", + " s.replace(\"(log) \", '')\n", + " for s in data\n", + "]\n", + "clean" + ] + }, + { + "cell_type": "markdown", + "id": "45fa082e", + "metadata": {}, + "source": [ + "You might be tempted to use the `lstrip` method:" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "id": "74e60a43", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['[2022-03-01T13:30:01] Log record 1',\n", + " '[2022-03-01T13:30:02] Log record 2',\n", + " '[2022-03-01T13:30:03] Log record 3',\n", + " '[2022-03-01T13:30:04] Log record 4']" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "clean = [\n", + " s.lstrip(\"(log) \")\n", + " for s in data\n", + "]\n", + "clean" + ] + }, + { + "cell_type": "markdown", + "id": "5e045ebc", + "metadata": {}, + "source": [ + "This appears to work, but `lstrip` (and `rstrip`) does not interpet `\"(log )\"` as a string, but rather a **sequence** of characters, and each one will be removed - so you might end up with this problem:" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "id": "ddc6e3a8", + "metadata": {}, + "outputs": [], + "source": [ + "data2 = [\n", + " \"(log) log: [2022-03-01T13:30:01] Log record 1\",\n", + " \"(log) log: [2022-03-01T13:30:02] Log record 2\",\n", + " \"(log) log: [2022-03-01T13:30:03] Log record 3\",\n", + " \"(log) log: [2022-03-01T13:30:04] Log record 4\",\n", + "]" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "id": "d87cd3c5", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[': [2022-03-01T13:30:01] Log record 1',\n", + " ': [2022-03-01T13:30:02] Log record 2',\n", + " ': [2022-03-01T13:30:03] Log record 3',\n", + " ': [2022-03-01T13:30:04] Log record 4']" + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "clean = [\n", + " s.lstrip(\"(log) \")\n", + " for s in data2\n", + "]\n", + "clean" + ] + }, + { + "cell_type": "markdown", + "id": "0f73aac3", + "metadata": {}, + "source": [ + "Now that removed a lot more than expected everything from those strings, unlike the replace, which will replace only the first occurrence by default:" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "id": "eaf8792b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['log: [2022-03-01T13:30:01] Log record 1',\n", + " 'log: [2022-03-01T13:30:02] Log record 2',\n", + " 'log: [2022-03-01T13:30:03] Log record 3',\n", + " 'log: [2022-03-01T13:30:04] Log record 4']" + ] + }, + "execution_count": 34, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "clean = [\n", + " s.replace(\"(log) \", '')\n", + " for s in data2\n", + "]\n", + "clean" + ] + }, + { + "cell_type": "markdown", + "id": "0aca0211", + "metadata": {}, + "source": [ + "Python 3,9 introduces two new string methods to do this without having to use `replace`, namely the `removeprefix()` and `removesuffix()` methods:" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "id": "b320eb46", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['[2022-03-01T13:30:01] Log record 1',\n", + " '[2022-03-01T13:30:02] Log record 2',\n", + " '[2022-03-01T13:30:03] Log record 3',\n", + " '[2022-03-01T13:30:04] Log record 4']" + ] + }, + "execution_count": 35, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "[\n", + " s.removeprefix(\"(log) \")\n", + " for s in data\n", + "]" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "id": "d4ebaf46", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['log: [2022-03-01T13:30:01] Log record 1',\n", + " 'log: [2022-03-01T13:30:02] Log record 2',\n", + " 'log: [2022-03-01T13:30:03] Log record 3',\n", + " 'log: [2022-03-01T13:30:04] Log record 4']" + ] + }, + "execution_count": 36, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "[\n", + " s.removeprefix(\"(log) \")\n", + " for s in data2\n", + "]" + ] + }, + { + "cell_type": "markdown", + "id": "f7a2ffd4", + "metadata": {}, + "source": [ + "Note that if the prefix (or suffix) is not found, nothing happens, the new string will be the same as the original (i.e. no exception is raised):" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "id": "829cb63b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'Python rocks!'" + ] + }, + "execution_count": 37, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "'Python rocks!'.removeprefix('Java')" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.10.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Part 1/Section 10 - Extras/16 - Relevant Python 3.10 Changes.ipynb b/Part 1/Section 10 - Extras/16 - Relevant Python 3.10 Changes.ipynb new file mode 100644 index 00000000..1e7fa9b4 --- /dev/null +++ b/Part 1/Section 10 - Extras/16 - Relevant Python 3.10 Changes.ipynb @@ -0,0 +1,991 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "c4d4fc49", + "metadata": {}, + "source": [ + "### Relevant Python 3.10 Changes" + ] + }, + { + "cell_type": "markdown", + "id": "cb1495a9", + "metadata": {}, + "source": [ + "The release of Python 3.10 has brought some new features.\n", + "\n", + "This is a summary of the ones _I_ deemed relevant to this course, and does **not** include all the changes!\n", + "\n", + "For full release details, see [here](https://docs.python.org/3/whatsnew/3.10.html)" + ] + }, + { + "cell_type": "markdown", + "id": "98c3ae0d", + "metadata": {}, + "source": [ + "Python 3.10 has improved it's error messages for syntax errors - the messages are more helpful and hopefully give you a better understanding of what may be syntactically wrong with your code." + ] + }, + { + "cell_type": "markdown", + "id": "4ae4aa37", + "metadata": {}, + "source": [ + "#### Structural Pattern Matching" + ] + }, + { + "cell_type": "markdown", + "id": "d18177a2", + "metadata": {}, + "source": [ + "One thing I often hear people ask, is, what's the Python equivalent of a `switch` statement." + ] + }, + { + "cell_type": "markdown", + "id": "36a77514", + "metadata": {}, + "source": [ + "Until now, the answer has alwasy been - there isn't one. Use `if...elif` constructs." + ] + }, + { + "cell_type": "markdown", + "id": "56fdb138", + "metadata": {}, + "source": [ + "Python 3.10 introduces a new language element (`match`) to implement something called **pattern matching**, that can be used to replicate this `switch` behavior you might be used to in other languages." + ] + }, + { + "cell_type": "markdown", + "id": "e1cf8b9d", + "metadata": {}, + "source": [ + "I'll cover some of the basics here, but you should refer to the Python [docs](https://docs.python.org/3/reference/compound_stmts.html#the-match-statement) for more information, as well as the [pep](https://peps.python.org/pep-0634/) for this feature and a [tutorial pep](https://peps.python.org/pep-0636/)." + ] + }, + { + "cell_type": "markdown", + "id": "1417630a", + "metadata": {}, + "source": [ + "Let's start with a simple `match` statement:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "bbecc554", + "metadata": {}, + "outputs": [], + "source": [ + "def respond(language):\n", + " match language:\n", + " case \"Java\":\n", + " return \"Hmm, coffee!\"\n", + " case \"Python\":\n", + " return \"I'm not scared of snakes!\"\n", + " case \"Rust\":\n", + " return \"Don't drink too much water!\"\n", + " case \"Go\":\n", + " return \"Collect $200\"\n", + " case _:\n", + " return \"I'm sorry...\"" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "d5669e92", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"I'm not scared of snakes!\"" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "respond(\"Python\")" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "90cecc86", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'Collect $200'" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "respond(\"Go\")" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "f9feb023", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"I'm sorry...\"" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "respond(\"COBOL\")" + ] + }, + { + "cell_type": "markdown", + "id": "ee7c21fb", + "metadata": {}, + "source": [ + "Here we were able to define a \"default\" match pattern by using the underscore (`_`) as our pattern - this `_` is called a **wildcard**." + ] + }, + { + "cell_type": "markdown", + "id": "b76575de", + "metadata": {}, + "source": [ + "So this is very much like the \"plain\" switch statement found in some other languages." + ] + }, + { + "cell_type": "markdown", + "id": "6e55dd9f", + "metadata": {}, + "source": [ + "But, this is where things get ineteresting, pattern matching can do much more than the simple example we just saw." + ] + }, + { + "cell_type": "markdown", + "id": "f8d495f2", + "metadata": {}, + "source": [ + "For example, you can have multiple pattern matching:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "6d5fcc0c", + "metadata": {}, + "outputs": [], + "source": [ + "def respond(language):\n", + " match language:\n", + " case \"Java\" | \"Javascript\":\n", + " return \"Love those braces!\"\n", + " case \"Python\":\n", + " return \"I'm a lumberjack and I don't need no braces\"\n", + " case _:\n", + " return \"I have no clue!\"" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "02cf21af", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'Love those braces!'" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "respond(\"Java\")" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "853368f3", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'Love those braces!'" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "respond(\"Javascript\")" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "066f985e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"I'm a lumberjack and I don't need no braces\"" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "respond(\"Python\")" + ] + }, + { + "cell_type": "markdown", + "id": "7fc43e7f", + "metadata": {}, + "source": [ + "We could match against one or more literals by using the OR pattern (`|`)" + ] + }, + { + "cell_type": "markdown", + "id": "36064a9c", + "metadata": {}, + "source": [ + "Let's look at one more example, this time matching **multiple values**." + ] + }, + { + "cell_type": "markdown", + "id": "31978910", + "metadata": {}, + "source": [ + "Suppose we have some kind of command language for driving a remote controlled robot in a maze, picking up and dropping items as it moves around. Our robot is very simple, it can move in only a few directions, and one step at a time. So to move forward three spaces, we would issue three `move forward` commands.\n", + "\n", + "Additional commands are `move backward`, `move left`, `move right`. We also have a few other commands our robot understands: `pick` and `drop` for picking up and dropping objects it might find." + ] + }, + { + "cell_type": "markdown", + "id": "63944ea5", + "metadata": {}, + "source": [ + "We might write a command interpreter this way:" + ] + }, + { + "cell_type": "markdown", + "id": "f9077f13", + "metadata": {}, + "source": [ + "Let's start by using some symbols to represent the robot's actions:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "2dde5d86", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'F': '→', 'B': '←', 'L': '↑', 'R': '↓', 'pick': '⤣', 'drop': '⤥'}" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "symbols = {\n", + " \"F\": \"\\u2192\", \n", + " \"B\": \"\\u2190\", \n", + " \"L\": \"\\u2191\", \n", + " \"R\": \"\\u2193\", \n", + " \"pick\": \"\\u2923\", \n", + " \"drop\": \"\\u2925\"\n", + "}\n", + "\n", + "symbols" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "faab4c09", + "metadata": {}, + "outputs": [], + "source": [ + "def op(command):\n", + " match command:\n", + " case \"move F\":\n", + " return symbols[\"F\"]\n", + " case \"move B\":\n", + " return symbols[\"B\"]\n", + " case \"move L\":\n", + " return symbols[\"L\"]\n", + " case \"move R\":\n", + " return symbols[\"R\"]\n", + " case \"pick\":\n", + " return symbols[\"pick\"]\n", + " case \"drop\":\n", + " return symbols[\"drop\"]\n", + " case _:\n", + " raise ValueError(f\"{command} does not compute!\")" + ] + }, + { + "cell_type": "markdown", + "id": "24555fab", + "metadata": {}, + "source": [ + "Then we could issue commands such as:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "b673a044", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'↑'" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "op(\"move L\")" + ] + }, + { + "cell_type": "markdown", + "id": "b411438f", + "metadata": {}, + "source": [ + "Or multiple sequences by maybe using a list of such commands, effectively creating a sequential program for our robot:" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "cf94014f", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['→', '→', '↑', '⤣', '↓', '↑', '→', '⤥']" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "[\n", + " op(\"move F\"),\n", + " op(\"move F\"),\n", + " op(\"move L\"),\n", + " op(\"pick\"),\n", + " op(\"move R\"),\n", + " op(\"move L\"),\n", + " op(\"move F\"),\n", + " op(\"drop\"),\n", + "]" + ] + }, + { + "cell_type": "markdown", + "id": "c520053b", + "metadata": {}, + "source": [ + "We could use something called **capturing** matched sub-patterns to simply our code somewhat:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "e7da20cf", + "metadata": {}, + "outputs": [], + "source": [ + "def op(command):\n", + " match command:\n", + " case [\"move\", (\"F\" | \"B\" | \"L\" |\"R\") as direction]:\n", + " return symbols[direction]\n", + " case \"pick\":\n", + " return symbols[\"pick\"]\n", + " case \"drop\":\n", + " return symvols[\"drop\"]\n", + " case _:\n", + " raise ValueError(f\"{command} does not compute!\")" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "c2a1c617", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'↑'" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "op([\"move\", \"L\"])" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "4edeb3dc", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'⤣'" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "op(\"pick\")" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "b13504aa", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "fly does not compute!\n" + ] + } + ], + "source": [ + "try:\n", + " op(\"fly\")\n", + "except ValueError as ex:\n", + " print(ex)" + ] + }, + { + "cell_type": "markdown", + "id": "6769c228", + "metadata": {}, + "source": [ + "This is kind of tedious, it would be nicer to write commands such as `move F F L` and `move R L F` instead." + ] + }, + { + "cell_type": "markdown", + "id": "055d2c60", + "metadata": {}, + "source": [ + "There are many ways we could solve this, but pattern matching on multiple values can be really useful here." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "372bdb17", + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "def op(command):\n", + " match command:\n", + " case ['move', *directions]:\n", + " return tuple(symbols[direction] for direction in directions)\n", + " case \"pick\":\n", + " return symbols[\"pick\"]\n", + " case \"drop\":\n", + " return symbols[\"drop\"]\n", + " case _:\n", + " raise ValueError(f\"{command} does not compute!\")" + ] + }, + { + "cell_type": "markdown", + "id": "c77bec44", + "metadata": {}, + "source": [ + "What happens here is that the pattern matcher will recognize the first word `move` and then interpret the remaining words collection them in the `directions` variable (so this syntax is very similar to unpacking)." + ] + }, + { + "cell_type": "markdown", + "id": "4ef78058", + "metadata": {}, + "source": [ + "We can now rewrite our program this way:" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "88c0c024", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[('→', '→', '↑'), '⤣', ('↓', '↑', '→'), '⤥']" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "[\n", + " op([\"move\", \"F\", \"F\", \"L\"]),\n", + " op(\"pick\"),\n", + " op([\"move\", \"R\", \"L\", \"F\"]),\n", + " op(\"drop\"),\n", + "]" + ] + }, + { + "cell_type": "markdown", + "id": "1ddc0f2e", + "metadata": {}, + "source": [ + "But now we have a slight problem:" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "ed30a762", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " 'up'\n" + ] + } + ], + "source": [ + "try:\n", + " op([\"move\", \"up\"])\n", + "except Exception as ex:\n", + " print(type(ex), ex)" + ] + }, + { + "cell_type": "markdown", + "id": "f6bbbd53", + "metadata": {}, + "source": [ + "We would rather just get our custom `ValueError`. To do this we can place a **guard** on our `case` for the `move` command, that will not only do the match but also test an additional condition:" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "8d441175", + "metadata": {}, + "outputs": [], + "source": [ + "def op(command):\n", + " match command:\n", + " case ['move', *directions] if set(directions) < symbols.keys():\n", + " return tuple(symbols[direction] for direction in directions)\n", + " case \"pick\":\n", + " return symbols[\"pick\"]\n", + " case \"drop\":\n", + " return symbols[\"drop\"]\n", + " case _:\n", + " raise ValueError(f\"{command} does not compute!\")" + ] + }, + { + "cell_type": "markdown", + "id": "441a53f6", + "metadata": {}, + "source": [ + "That `if ` statement (the **guard**) will only let the case block execute if the match is true **and** that `if` expression evaludates to `True`:" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "708c5d16", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " ['move', 'up'] does not compute!\n" + ] + } + ], + "source": [ + "try:\n", + " op([\"move\", \"up\"])\n", + "except Exception as ex:\n", + " print(type(ex), ex)" + ] + }, + { + "cell_type": "markdown", + "id": "1ecf5f83", + "metadata": {}, + "source": [ + "There are many other ways we could have done this - probably better than this, but this was to illustrate how the multiple value matching can work!" + ] + }, + { + "cell_type": "markdown", + "id": "84bec678", + "metadata": {}, + "source": [ + "I urge you to read at least this [tutorial (pep 636)](https://peps.python.org/pep-0636/) on pattern matching." + ] + }, + { + "cell_type": "markdown", + "id": "5b6a7b09", + "metadata": {}, + "source": [ + "#### The `zip` Function" + ] + }, + { + "cell_type": "markdown", + "id": "1294e5f6", + "metadata": {}, + "source": [ + "We use the built-in `zip` function all the time. As we know, the `zip` will stop iterating after the first of the iterables provided in the arguments is exhausted:" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "e33aa60d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[('a', 10), ('b', 20), ('c', 30)]" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "l1 = ['a', 'b', 'c']\n", + "l2 = [10, 20, 30, 40]\n", + "\n", + "list(zip(l1, l2))" + ] + }, + { + "cell_type": "markdown", + "id": "7d310af8", + "metadata": {}, + "source": [ + "As you can see the last element of `l2` is not included in the result since `l1` only had three elements." + ] + }, + { + "cell_type": "markdown", + "id": "a9cf9570", + "metadata": {}, + "source": [ + "We can also use the `zip_longest` function in the `itertools` module to iterate over the longest iterable, providing some default value for any other iterable that has been exhausted:" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "aefa3055", + "metadata": {}, + "outputs": [], + "source": [ + "from itertools import zip_longest" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "ce4779c9", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[('a', 10), ('b', 20), ('c', 30), ('???', 40)]" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list(zip_longest(l1, l2, fillvalue='???'))" + ] + }, + { + "cell_type": "markdown", + "id": "0eb2f702", + "metadata": {}, + "source": [ + "But what if we want to only zip iterables that have the **same** length? We would need to test the length of each iterable first - but if those were iterators instead of iterables, we will have exhausted the iterator, and `zip` would come back \"empty\":" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "3b704410", + "metadata": {}, + "outputs": [], + "source": [ + "l1 = (i ** 2 for i in range(4))\n", + "l2 = (i ** 3 for i in range(3))" + ] + }, + { + "cell_type": "markdown", + "id": "da0d866f", + "metadata": {}, + "source": [ + "We could test to see if `l1` and `l2` are the same length:" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "bc7d34c9", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "len(list(l1)) == len(list(l2))" + ] + }, + { + "cell_type": "markdown", + "id": "0a4a1974", + "metadata": {}, + "source": [ + "But, if we now try to `zip` them:" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "1c3928b9", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[]" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list(zip(l1, l2))" + ] + }, + { + "cell_type": "markdown", + "id": "9ee310e9", + "metadata": {}, + "source": [ + "In Python 3.10, the `zip` function now has a keyword-only parameter called `strict` that will just do the zip, but throw an exception if one of the arguments get exhausted before the others:" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "54722489", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "zip() argument 2 is shorter than argument 1\n" + ] + } + ], + "source": [ + "l1 = (i ** 2 for i in range(4))\n", + "l2 = (i ** 3 for i in range(3))\n", + "\n", + "try:\n", + " list(zip(l1, l2, strict=True))\n", + "except ValueError as ex:\n", + " print(ex)" + ] + }, + { + "cell_type": "markdown", + "id": "2635211a", + "metadata": {}, + "source": [ + "And works just fine if the arguments all have the same length:" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "7fb672cb", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[(0, 0, 0), (1, 1, 1), (4, 8, 16), (9, 27, 81)]" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "l1 = (i ** 2 for i in range(4))\n", + "l2 = (i ** 3 for i in range(4))\n", + "l3 = (i ** 4 for i in range(4))\n", + "\n", + "list(zip(l1, l2, l3))" + ] + }, + { + "cell_type": "markdown", + "id": "501fec3b", + "metadata": {}, + "source": [ + "So why is this useful?" + ] + }, + { + "cell_type": "markdown", + "id": "dab4260e", + "metadata": {}, + "source": [ + "In **many** cases, our code zips iterables that we expect to be of the same length. To avoid bugs in our program, we should check that this condition is true, otherwise zip will silently just zip based on the shortest one. But as we saw with iterators, that can be difficult to do without exhausting the very iterators we are trying to zip. (it can be done, it's just more code)." + ] + }, + { + "cell_type": "markdown", + "id": "c73e5ad1", + "metadata": {}, + "source": [ + "So, if you are one of the lucky devs that gets to write Python 3.10 (or higher :-) ) code, you can just use `strict` whenever you zip things together and expect that they are all of the same length. Much easier to do it this way (and, as we discuss in Exception handling, falls into the category of \"ask forgiveness later\" which we saw was the preferred way (in general) to handle exceptions in our apps, as opposed to the \"look before you leap\" approach we would have to use to test the argument lengths." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "400a56c1", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.10.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Pipfile b/Pipfile index 9faacc84..6f95c016 100644 --- a/Pipfile +++ b/Pipfile @@ -5,8 +5,10 @@ name = "pypi" [packages] jupyter = "*" +pytz = "*" +python-dateutil="*" [dev-packages] [requires] -python_version = "3.9" +python_version = "3.10" diff --git a/Pipfile.lock b/Pipfile.lock index 65cade01..c155c00c 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,11 +1,11 @@ { "_meta": { "hash": { - "sha256": "3ff35502dbd4d7f84d0000d0a78dfba5c47b879759484b6ca31807310dcf969c" + "sha256": "acd32c6a9770319887f37b7f5048f03ac517a4926693ec3c5b953e7dc619c697" }, "pipfile-spec": 6, "requires": { - "python_version": "3.9" + "python_version": "3.10" }, "sources": [ { @@ -81,34 +81,13 @@ ], "version": "==0.2.0" }, - "black": { - "hashes": [ - "sha256:07e5c049442d7ca1a2fc273c79d1aecbbf1bc858f62e8184abe1ad175c4f7cc2", - "sha256:0e21e1f1efa65a50e3960edd068b6ae6d64ad6235bd8bfea116a03b21836af71", - "sha256:1297c63b9e1b96a3d0da2d85d11cd9bf8664251fd69ddac068b98dc4f34f73b6", - "sha256:228b5ae2c8e3d6227e4bde5920d2fc66cc3400fde7bcc74f480cb07ef0b570d5", - "sha256:2d6f331c02f0f40aa51a22e479c8209d37fcd520c77721c034517d44eecf5912", - "sha256:2ff96450d3ad9ea499fc4c60e425a1439c2120cbbc1ab959ff20f7c76ec7e866", - "sha256:3524739d76b6b3ed1132422bf9d82123cd1705086723bc3e235ca39fd21c667d", - "sha256:35944b7100af4a985abfcaa860b06af15590deb1f392f06c8683b4381e8eeaf0", - "sha256:373922fc66676133ddc3e754e4509196a8c392fec3f5ca4486673e685a421321", - "sha256:5fa1db02410b1924b6749c245ab38d30621564e658297484952f3d8a39fce7e8", - "sha256:6f2f01381f91c1efb1451998bd65a129b3ed6f64f79663a55fe0e9b74a5f81fd", - "sha256:742ce9af3086e5bd07e58c8feb09dbb2b047b7f566eb5f5bc63fd455814979f3", - "sha256:7835fee5238fc0a0baf6c9268fb816b5f5cd9b8793423a75e8cd663c48d073ba", - "sha256:8871fcb4b447206904932b54b567923e5be802b9b19b744fdff092bd2f3118d0", - "sha256:a7c0192d35635f6fc1174be575cb7915e92e5dd629ee79fdaf0dcfa41a80afb5", - "sha256:b1a5ed73ab4c482208d20434f700d514f66ffe2840f63a6252ecc43a9bc77e8a", - "sha256:c8226f50b8c34a14608b848dc23a46e5d08397d009446353dad45e04af0c8e28", - "sha256:ccad888050f5393f0d6029deea2a33e5ae371fd182a697313bdbd835d3edaf9c", - "sha256:dae63f2dbf82882fa3b2a3c49c32bffe144970a573cd68d247af6560fc493ae1", - "sha256:e2f69158a7d120fd641d1fa9a921d898e20d52e44a74a6fbbcc570a62a6bc8ab", - "sha256:efbadd9b52c060a8fc3b9658744091cb33c31f830b3f074422ed27bad2b18e8f", - "sha256:f5660feab44c2e3cb24b2419b998846cbb01c23c7fe645fee45087efa3da2d61", - "sha256:fdb8754b453fb15fad3f72cd9cad3e16776f0964d67cf30ebcbf10327a3777a3" + "beautifulsoup4": { + "hashes": [ + "sha256:9a315ce70049920ea4572a4055bc4bd700c940521d36fc858205ad4fcde149bf", + "sha256:c23ad23c521d818955a4151a67d81580319d4bf548d3d49f4223ae041ff98891" ], - "markers": "python_full_version >= '3.6.2'", - "version": "==22.1.0" + "markers": "python_version >= '3.1'", + "version": "==4.10.0" }, "bleach": { "hashes": [ @@ -173,14 +152,6 @@ ], "version": "==1.15.0" }, - "click": { - "hashes": [ - "sha256:6a7a62563bbfabfda3a38f3023a1db4a35978c0abd76f6c9605ecd6554d6d9b1", - "sha256:8458d7b1287c5fb128c90e23381cf99dcde74beaf6c7ff6384ce84d6fe090adb" - ], - "markers": "python_version >= '3.6'", - "version": "==8.0.4" - }, "debugpy": { "hashes": [ "sha256:01e98c594b3e66d529e40edf314f849cd1a21f7a013298df58cd8e263bf8e184", @@ -234,26 +205,26 @@ }, "executing": { "hashes": [ - "sha256:32fc6077b103bd19e6494a72682d66d5763cf20a106d5aa7c5ccbea4e47b0df7", - "sha256:c23bf42e9a7b9b212f185b1b2c3c91feb895963378887bb10e64a2e612ec0023" + "sha256:c6554e21c6b060590a6d3be4b82fb78f8f0194d809de5ea7df1c093763311501", + "sha256:d1eef132db1b83649a3905ca6dd8897f71ac6f8cac79a7e58a1a09cf137546c9" ], - "version": "==0.8.2" + "version": "==0.8.3" }, "ipykernel": { "hashes": [ - "sha256:4fae9df6e192837552b2406a6052d707046dd2e153860be73c68484bacba18ed", - "sha256:f95070a2dfd3147f8ab19f18ee46733310813758593745e07ec18fb08b409f1d" + "sha256:4c3cc8cb359f2ead70c30f5504971c0d285e2c1c699d2ce9af0216fe9c9fb17c", + "sha256:c977cff576b8425a68d3a6916510903833f0f25ed8d5c282a0c51c35de27bd47" ], "markers": "python_version >= '3.7'", - "version": "==6.9.1" + "version": "==6.9.2" }, "ipython": { "hashes": [ - "sha256:ab564d4521ea8ceaac26c3a2c6e5ddbca15c8848fd5a5cc325f960da88d42974", - "sha256:c503a0dd6ccac9c8c260b211f2dd4479c042b49636b097cc9a0d55fe62dff64c" + "sha256:6f56bfaeaa3247aa3b9cd3b8cbab3a9c0abf7428392f97b21902d12b2f42a381", + "sha256:8138762243c9b3a3ffcf70b37151a2a35c23d3a29f9743878c33624f4207be3d" ], "markers": "python_version >= '3.8'", - "version": "==8.0.1" + "version": "==8.1.1" }, "ipython-genutils": { "hashes": [ @@ -264,10 +235,10 @@ }, "ipywidgets": { "hashes": [ - "sha256:00974f7cb4d5f8d494c19810fedb9fa9b64bffd3cda7c2be23c133a1ad3c99c5", - "sha256:d258f582f915c62ea91023299603be095de19afb5ee271698f88327b9fe9bf43" + "sha256:ab4a5596855a88b83761921c768707d65e5847068139bc1729ddfe834703542a", + "sha256:e58ff58bc94d481e91ecb6e13a5cb96a87b6b8ade135e055603d0ca24593df38" ], - "version": "==7.6.5" + "version": "==7.7.0" }, "jedi": { "hashes": [ @@ -312,11 +283,11 @@ }, "jupyter-console": { "hashes": [ - "sha256:242248e1685039cd8bff2c2ecb7ce6c1546eb50ee3b08519729e6e881aec19c7", - "sha256:7799c4ea951e0e96ba8260575423cb323ea5a03fcf5503560fa3e15748869e27" + "sha256:55f32626b0be647a85e3217ddcdb22db69efc79e8b403b9771eb9ecc696019b5", + "sha256:e630bcb682c0088dda45688ad7c2424d4a825c8acf494cb036ced03ed0424841" ], "markers": "python_version >= '3.6'", - "version": "==6.4.0" + "version": "==6.4.3" }, "jupyter-core": { "hashes": [ @@ -335,57 +306,57 @@ }, "jupyterlab-widgets": { "hashes": [ - "sha256:7885092b2b96bf189c3a705cc3c412a4472ec5e8382d0b47219a66cccae73cfa", - "sha256:f5d9efface8ec62941173ba1cffb2edd0ecddc801c11ae2931e30b50492eb8f7" + "sha256:c2a9bd3789f120f64d73268c066ed3b000c56bc1dda217be5cdc43e7b4ebad3f", + "sha256:d5f41bc1713795385f718d44dcba47e1e1473c6289f28a95aa6b2c0782ee372a" ], "markers": "python_version >= '3.6'", - "version": "==1.0.2" + "version": "==1.1.0" }, "markupsafe": { "hashes": [ - "sha256:023af8c54fe63530545f70dd2a2a7eed18d07a9a77b94e8bf1e2ff7f252db9a3", - "sha256:09c86c9643cceb1d87ca08cdc30160d1b7ab49a8a21564868921959bd16441b8", - "sha256:142119fb14a1ef6d758912b25c4e803c3ff66920635c44078666fe7cc3f8f759", - "sha256:1d1fb9b2eec3c9714dd936860850300b51dbaa37404209c8d4cb66547884b7ed", - "sha256:204730fd5fe2fe3b1e9ccadb2bd18ba8712b111dcabce185af0b3b5285a7c989", - "sha256:24c3be29abb6b34052fd26fc7a8e0a49b1ee9d282e3665e8ad09a0a68faee5b3", - "sha256:290b02bab3c9e216da57c1d11d2ba73a9f73a614bbdcc027d299a60cdfabb11a", - "sha256:3028252424c72b2602a323f70fbf50aa80a5d3aa616ea6add4ba21ae9cc9da4c", - "sha256:30c653fde75a6e5eb814d2a0a89378f83d1d3f502ab710904ee585c38888816c", - "sha256:3cace1837bc84e63b3fd2dfce37f08f8c18aeb81ef5cf6bb9b51f625cb4e6cd8", - "sha256:4056f752015dfa9828dce3140dbadd543b555afb3252507348c493def166d454", - "sha256:454ffc1cbb75227d15667c09f164a0099159da0c1f3d2636aa648f12675491ad", - "sha256:598b65d74615c021423bd45c2bc5e9b59539c875a9bdb7e5f2a6b92dfcfc268d", - "sha256:599941da468f2cf22bf90a84f6e2a65524e87be2fce844f96f2dd9a6c9d1e635", - "sha256:5ddea4c352a488b5e1069069f2f501006b1a4362cb906bee9a193ef1245a7a61", - "sha256:62c0285e91414f5c8f621a17b69fc0088394ccdaa961ef469e833dbff64bd5ea", - "sha256:679cbb78914ab212c49c67ba2c7396dc599a8479de51b9a87b174700abd9ea49", - "sha256:6e104c0c2b4cd765b4e83909cde7ec61a1e313f8a75775897db321450e928cce", - "sha256:736895a020e31b428b3382a7887bfea96102c529530299f426bf2e636aacec9e", - "sha256:75bb36f134883fdbe13d8e63b8675f5f12b80bb6627f7714c7d6c5becf22719f", - "sha256:7d2f5d97fcbd004c03df8d8fe2b973fe2b14e7bfeb2cfa012eaa8759ce9a762f", - "sha256:80beaf63ddfbc64a0452b841d8036ca0611e049650e20afcb882f5d3c266d65f", - "sha256:84ad5e29bf8bab3ad70fd707d3c05524862bddc54dc040982b0dbcff36481de7", - "sha256:8da5924cb1f9064589767b0f3fc39d03e3d0fb5aa29e0cb21d43106519bd624a", - "sha256:961eb86e5be7d0973789f30ebcf6caab60b844203f4396ece27310295a6082c7", - "sha256:96de1932237abe0a13ba68b63e94113678c379dca45afa040a17b6e1ad7ed076", - "sha256:a0a0abef2ca47b33fb615b491ce31b055ef2430de52c5b3fb19a4042dbc5cadb", - "sha256:b2a5a856019d2833c56a3dcac1b80fe795c95f401818ea963594b345929dffa7", - "sha256:b8811d48078d1cf2a6863dafb896e68406c5f513048451cd2ded0473133473c7", - "sha256:c532d5ab79be0199fa2658e24a02fce8542df196e60665dd322409a03db6a52c", - "sha256:d3b64c65328cb4cd252c94f83e66e3d7acf8891e60ebf588d7b493a55a1dbf26", - "sha256:d4e702eea4a2903441f2735799d217f4ac1b55f7d8ad96ab7d4e25417cb0827c", - "sha256:d5653619b3eb5cbd35bfba3c12d575db2a74d15e0e1c08bf1db788069d410ce8", - "sha256:d66624f04de4af8bbf1c7f21cc06649c1c69a7f84109179add573ce35e46d448", - "sha256:e67ec74fada3841b8c5f4c4f197bea916025cb9aa3fe5abf7d52b655d042f956", - "sha256:e6f7f3f41faffaea6596da86ecc2389672fa949bd035251eab26dc6697451d05", - "sha256:f02cf7221d5cd915d7fa58ab64f7ee6dd0f6cddbb48683debf5d04ae9b1c2cc1", - "sha256:f0eddfcabd6936558ec020130f932d479930581171368fd728efcfb6ef0dd357", - "sha256:fabbe18087c3d33c5824cb145ffca52eccd053061df1d79d4b66dafa5ad2a5ea", - "sha256:fc3150f85e2dbcf99e65238c842d1cfe69d3e7649b19864c1cc043213d9cd730" + "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003", + "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88", + "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5", + "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7", + "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a", + "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603", + "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1", + "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135", + "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247", + "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6", + "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601", + "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77", + "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02", + "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e", + "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63", + "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f", + "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980", + "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b", + "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812", + "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff", + "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96", + "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1", + "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925", + "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a", + "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6", + "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e", + "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f", + "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4", + "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f", + "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3", + "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c", + "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a", + "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417", + "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a", + "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a", + "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37", + "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452", + "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933", + "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a", + "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7" ], "markers": "python_version >= '3.7'", - "version": "==2.1.0" + "version": "==2.1.1" }, "matplotlib-inline": { "hashes": [ @@ -402,36 +373,29 @@ ], "version": "==0.8.4" }, - "mypy-extensions": { - "hashes": [ - "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d", - "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8" - ], - "version": "==0.4.3" - }, "nbclient": { "hashes": [ - "sha256:03e857bea3012377289daa1e1c1651f4fc0295bcd109ccd36a337efcdbebaed7", - "sha256:751516992f34b58172bad54eef1e4bf7e4f4460d58e255ca1a4e5c9649476007" + "sha256:40c52c9b5e3c31faecaee69f202b3f53e38d7c1c563de0fadde9d7eda0fdafe8", + "sha256:47ac905af59379913c1f8f541098d2550153cf8dc58553cbe18c702b181518b0" ], "markers": "python_version >= '3.7'", - "version": "==0.5.11" + "version": "==0.5.13" }, "nbconvert": { "hashes": [ - "sha256:7b006ae9979af56200e7fa3db39d9d12c99e811e8843b05dbe518e5b754bcb2e", - "sha256:eb2803db18f6facce6bf3b01b684fe47907994bd156d15eaccdf011e3d7f8164" + "sha256:c0c13d11378e13f72b9cd509c008383dca4051c228e4985f75023b2a5d82fc9f", + "sha256:ee0dfe34bbd1082ac9bfc750aae3c73fcbc34a70c5574c6986ff83c10a3541fd" ], "markers": "python_version >= '3.7'", - "version": "==6.4.2" + "version": "==6.4.4" }, "nbformat": { "hashes": [ - "sha256:b516788ad70771c6250977c1374fcca6edebe6126fd2adb5a69aa5c2356fd1c8", - "sha256:eb8447edd7127d043361bc17f2f5a807626bc8e878c7709a1c647abda28a9171" + "sha256:3e30424e8291b2188347f5c3ba5273ed3766f12f8c5137c2e456a0815f36e785", + "sha256:93df0b9c67221d38fb970c48f6d361819a6c388299a0ef3171bbb912edfe1324" ], - "markers": "python_version >= '3.5'", - "version": "==5.1.3" + "markers": "python_version >= '3.7'", + "version": "==5.2.0" }, "nest-asyncio": { "hashes": [ @@ -443,11 +407,11 @@ }, "notebook": { "hashes": [ - "sha256:1e985c9dc6f678bdfffb9dc657306b5469bfa62d73e03f74e8defbf76d284312", - "sha256:3e702fcc54b8ae597533c3864793b7a1e971dec9e112f67235828d8a798fd654" + "sha256:2408a76bc6289283a8eecfca67e298ec83c67db51a4c2e1b713dd180bb39e90e", + "sha256:49cead814bff0945fcb2ee07579259418672ac175d3dc3d8102a4b0a656ed4df" ], "markers": "python_version >= '3.6'", - "version": "==6.4.8" + "version": "==6.4.10" }, "packaging": { "hashes": [ @@ -473,13 +437,6 @@ "markers": "python_version >= '3.6'", "version": "==0.8.3" }, - "pathspec": { - "hashes": [ - "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a", - "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1" - ], - "version": "==0.9.0" - }, "pexpect": { "hashes": [ "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937", @@ -495,14 +452,6 @@ ], "version": "==0.7.5" }, - "platformdirs": { - "hashes": [ - "sha256:7535e70dfa32e84d4b34996ea99c5e432fa29a708d0f4e394bbcb2a8faa4f16d", - "sha256:bcae7cab893c2d310a711b70b24efb93334febe65f8de776ee320b517471e227" - ], - "markers": "python_version >= '3.7'", - "version": "==2.5.1" - }, "prometheus-client": { "hashes": [ "sha256:357a447fd2359b0a1d2e9b311a0c5778c330cfbe186d880ad5a6b39884652316", @@ -519,6 +468,44 @@ "markers": "python_full_version >= '3.6.2'", "version": "==3.0.28" }, + "psutil": { + "hashes": [ + "sha256:072664401ae6e7c1bfb878c65d7282d4b4391f1bc9a56d5e03b5a490403271b5", + "sha256:1070a9b287846a21a5d572d6dddd369517510b68710fca56b0e9e02fd24bed9a", + "sha256:1d7b433519b9a38192dfda962dd8f44446668c009833e1429a52424624f408b4", + "sha256:3151a58f0fbd8942ba94f7c31c7e6b310d2989f4da74fcbf28b934374e9bf841", + "sha256:32acf55cb9a8cbfb29167cd005951df81b567099295291bcfd1027365b36591d", + "sha256:3611e87eea393f779a35b192b46a164b1d01167c9d323dda9b1e527ea69d697d", + "sha256:3d00a664e31921009a84367266b35ba0aac04a2a6cad09c550a89041034d19a0", + "sha256:4e2fb92e3aeae3ec3b7b66c528981fd327fb93fd906a77215200404444ec1845", + "sha256:539e429da49c5d27d5a58e3563886057f8fc3868a5547b4f1876d9c0f007bccf", + "sha256:55ce319452e3d139e25d6c3f85a1acf12d1607ddedea5e35fb47a552c051161b", + "sha256:58c7d923dc209225600aec73aa2c4ae8ea33b1ab31bc11ef8a5933b027476f07", + "sha256:7336292a13a80eb93c21f36bde4328aa748a04b68c13d01dfddd67fc13fd0618", + "sha256:742c34fff804f34f62659279ed5c5b723bb0195e9d7bd9907591de9f8f6558e2", + "sha256:7641300de73e4909e5d148e90cc3142fb890079e1525a840cf0dfd39195239fd", + "sha256:76cebf84aac1d6da5b63df11fe0d377b46b7b500d892284068bacccf12f20666", + "sha256:7779be4025c540d1d65a2de3f30caeacc49ae7a2152108adeaf42c7534a115ce", + "sha256:7d190ee2eaef7831163f254dc58f6d2e2a22e27382b936aab51c835fc080c3d3", + "sha256:8293942e4ce0c5689821f65ce6522ce4786d02af57f13c0195b40e1edb1db61d", + "sha256:869842dbd66bb80c3217158e629d6fceaecc3a3166d3d1faee515b05dd26ca25", + "sha256:90a58b9fcae2dbfe4ba852b57bd4a1dded6b990a33d6428c7614b7d48eccb492", + "sha256:9b51917c1af3fa35a3f2dabd7ba96a2a4f19df3dec911da73875e1edaf22a40b", + "sha256:b2237f35c4bbae932ee98902a08050a27821f8f6dfa880a47195e5993af4702d", + "sha256:c3400cae15bdb449d518545cbd5b649117de54e3596ded84aacabfbb3297ead2", + "sha256:c51f1af02334e4b516ec221ee26b8fdf105032418ca5a5ab9737e8c87dafe203", + "sha256:cb8d10461c1ceee0c25a64f2dd54872b70b89c26419e147a05a10b753ad36ec2", + "sha256:d62a2796e08dd024b8179bd441cb714e0f81226c352c802fca0fd3f89eeacd94", + "sha256:df2c8bd48fb83a8408c8390b143c6a6fa10cb1a674ca664954de193fdcab36a9", + "sha256:e5c783d0b1ad6ca8a5d3e7b680468c9c926b804be83a3a8e95141b05c39c9f64", + "sha256:e9805fed4f2a81de98ae5fe38b75a74c6e6ad2df8a5c479594c7629a1fe35f56", + "sha256:ea42d747c5f71b5ccaa6897b216a7dadb9f52c72a0fe2b872ef7d3e1eacf3ba3", + "sha256:ef216cc9feb60634bda2f341a9559ac594e2eeaadd0ba187a4c2eb5b5d40b91c", + "sha256:ff0d41f8b3e9ebb6b6110057e40019a432e96aae2008951121ba4e56040b84f3" + ], + "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==5.9.0" + }, "ptyprocess": { "hashes": [ "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", @@ -588,9 +575,17 @@ "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'", + "index": "pypi", "version": "==2.8.2" }, + "pytz": { + "hashes": [ + "sha256:1e760e2fe6a8163bc0b3d9a19c4f84342afa0a2affebfaa84b01b978a02ecaa7", + "sha256:e68985985296d9a66a881eb3193b0906246245294a881e7c8afe623866ac6a5c" + ], + "index": "pypi", + "version": "==2022.1" + }, "pyzmq": { "hashes": [ "sha256:08c4e315a76ef26eb833511ebf3fa87d182152adf43dedee8d79f998a2162a0b", @@ -669,20 +664,28 @@ }, "setuptools": { "hashes": [ - "sha256:2347b2b432c891a863acadca2da9ac101eae6169b1d3dfee2ec605ecd50dbfe5", - "sha256:e4f30b9f84e5ab3decf945113119649fec09c1fc3507c6ebffec75646c56e62b" + "sha256:6599055eeb23bfef457d5605d33a4d68804266e6cb430b0fb12417c5efeae36c", + "sha256:782ef48d58982ddb49920c11a0c5c9c0b02e7d7d1c2ad0aa44e1a1e133051c96" ], "markers": "python_version >= '3.7'", - "version": "==60.9.3" + "version": "==60.10.0" }, "six": { "hashes": [ "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'", + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==1.16.0" }, + "soupsieve": { + "hashes": [ + "sha256:1a3cca2617c6b38c0343ed661b1fa5de5637f257d4fe22bd9f1338010a1efefb", + "sha256:b8d49b1cd4f037c7082a9683dfa1801aa2597fb11c3a1155b7a5b94829b4f1f9" + ], + "markers": "python_version >= '3.6'", + "version": "==2.3.1" + }, "stack-data": { "hashes": [ "sha256:45692d41bd633a9503a5195552df22b583caf16f0b27c4e58c98d88c8b648e12", @@ -692,27 +695,19 @@ }, "terminado": { "hashes": [ - "sha256:5b82b5c6e991f0705a76f961f43262a7fb1e55b093c16dca83f16384a7f39b7b", - "sha256:f446b522b50a7aa68b5def0a02893978fb48cb82298b0ebdae13003c6ee6f198" + "sha256:874d4ea3183536c1782d13c7c91342ef0cf4e5ee1d53633029cbc972c8760bd8", + "sha256:94d1cfab63525993f7d5c9b469a50a18d0cdf39435b59785715539dd41e36c0d" ], "markers": "python_version >= '3.7'", - "version": "==0.13.1" + "version": "==0.13.3" }, "testpath": { "hashes": [ - "sha256:1acf7a0bcd3004ae8357409fc33751e16d37ccc650921da1094a86581ad1e417", - "sha256:8044f9a0bab6567fc644a3593164e872543bb44225b0e24846e2c89237937589" + "sha256:2f1b97e6442c02681ebe01bd84f531028a7caea1af3825000f52345c30285e0f", + "sha256:8ada9f80a2ac6fb0391aa7cdb1a7d11cfa8429f693eda83f74dde570fe6fa639" ], "markers": "python_version >= '3.5'", - "version": "==0.5.0" - }, - "tomli": { - "hashes": [ - "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", - "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f" - ], - "markers": "python_version >= '3.7'", - "version": "==2.0.1" + "version": "==0.6.0" }, "tornado": { "hashes": [ @@ -769,14 +764,6 @@ "markers": "python_version >= '3.7'", "version": "==5.1.1" }, - "typing-extensions": { - "hashes": [ - "sha256:1a9462dcc3347a79b1f1c0271fbe79e844580bb598bafa1ed208b94da3cdcd42", - "sha256:21c85e0fe4b9a155d0799430b0ad741cdce7e359660ccbd8b530613e8df88ce2" - ], - "markers": "python_version < '3.10'", - "version": "==4.1.1" - }, "wcwidth": { "hashes": [ "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784", @@ -793,10 +780,10 @@ }, "widgetsnbextension": { "hashes": [ - "sha256:763a9fdc836d141fa080005a886d63f66f73d56dba1fb5961afc239c77708569", - "sha256:e0731a60ba540cd19bbbefe771a9076dcd2dde90713a8f87f27f53f2d1db7727" + "sha256:4fd321cad39fdcf8a8e248a657202d42917ada8e8ed5dd3f60f073e0d54ceabd", + "sha256:e84a7a9fcb9baf3d57106e184a7389a8f8eb935bf741a5eb9d60aa18cc029a80" ], - "version": "==3.5.2" + "version": "==3.6.0" } }, "develop": {} From 62adf0be55e70f821c6691747492dd4d3c6a256c Mon Sep 17 00:00:00 2001 From: fbaptiste <6551123+fbaptiste@users.noreply.github.com> Date: Mon, 21 Mar 2022 06:32:21 -0700 Subject: [PATCH 10/27] Python updates --- ...=> 01 - Relevant Python 3.8 Changes.ipynb} | 0 ...=> 02 - Relevant Python 3.9 Changes.ipynb} | 0 ...> 03 - Relevant Python 3.10 Changes.ipynb} | 0 ...ipynb => 04 - Python 3.6 Highlights.ipynb} | 0 ... - Python 3.6 - Dictionary Ordering.ipynb} | 0 ...of kwargs - Named Tuple Application.ipynb} | 0 ... - Underscores and Numeric Literals.ipynb} | 0 ...pynb => 08 - Python 3.6 - f-Strings.ipynb} | 0 ...om Seeds.ipynb => 09 - Random Seeds.ipynb} | 0 ...hoices.ipynb => 10 - Random Choices.ipynb} | 0 ...amples.ipynb => 11 - Random Samples.ipynb} | 0 ...nb => 12 - Timing code using timeit.ipynb} | 0 ...t Use args and kwargs Names Blindly.ipynb} | 0 .../example1.py | 0 .../example10.py | 0 .../example2.py | 0 .../example3.py | 0 .../example4.py | 0 .../example5.py | 0 .../example6.py | 0 .../example7.py | 0 .../example8.py | 0 .../example9.py | 0 ...tinel Values for Parameter Defaults.ipynb} | 0 ...imulating a simple Switch in Python.ipynb} | 0 .../01 - Relevant Python 3.8 Changes.ipynb | 1476 +++++++++++ .../02 - Relevant Python 3.9 Changes.ipynb | 2343 +++++++++++++++++ .../03 - Relevant Python 3.10 Changes.ipynb | 991 +++++++ .../01 - Relevant Python 3.8 Changes.ipynb | 1476 +++++++++++ .../02 - Relevant Python 3.9 Changes.ipynb | 2343 +++++++++++++++++ .../03 - Relevant Python 3.10 Changes.ipynb | 991 +++++++ ...Type.ipynb => 04 - MappingProxyType.ipynb} | 0 .../01 - Relevant Python 3.8 Changes.ipynb | 1476 +++++++++++ .../02 - Relevant Python 3.9 Changes.ipynb | 2343 +++++++++++++++++ .../03 - Relevant Python 3.10 Changes.ipynb | 991 +++++++ 35 files changed, 14430 insertions(+) rename Part 1/Section 10 - Extras/{14 - Relevant Python 3.8 Changes.ipynb => 01 - Relevant Python 3.8 Changes.ipynb} (100%) rename Part 1/Section 10 - Extras/{15 - Relevant Python 3.9 Changes.ipynb => 02 - Relevant Python 3.9 Changes.ipynb} (100%) rename Part 1/Section 10 - Extras/{16 - Relevant Python 3.10 Changes.ipynb => 03 - Relevant Python 3.10 Changes.ipynb} (100%) rename Part 1/Section 10 - Extras/{01 - Python 3.6 Highlights.ipynb => 04 - Python 3.6 Highlights.ipynb} (100%) rename Part 1/Section 10 - Extras/{02 - Python 3.6 - Dictionary Ordering.ipynb => 05 - Python 3.6 - Dictionary Ordering.ipynb} (100%) rename Part 1/Section 10 - Extras/{03 - Python 3.6 - Preserved Order of kwargs - Named Tuple Application.ipynb => 06 - Python 3.6 - Preserved Order of kwargs - Named Tuple Application.ipynb} (100%) rename Part 1/Section 10 - Extras/{04 - Python 3.6 - Underscores and Numeric Literals.ipynb => 07 - Python 3.6 - Underscores and Numeric Literals.ipynb} (100%) rename Part 1/Section 10 - Extras/{05 - Python 3.6 - f-Strings.ipynb => 08 - Python 3.6 - f-Strings.ipynb} (100%) rename Part 1/Section 10 - Extras/{06 - Random Seeds.ipynb => 09 - Random Seeds.ipynb} (100%) rename Part 1/Section 10 - Extras/{07 - Random Choices.ipynb => 10 - Random Choices.ipynb} (100%) rename Part 1/Section 10 - Extras/{08 - Random Samples.ipynb => 11 - Random Samples.ipynb} (100%) rename Part 1/Section 10 - Extras/{09 - Timing code using timeit.ipynb => 12 - Timing code using timeit.ipynb} (100%) rename Part 1/Section 10 - Extras/{10 - Don't Use args and kwargs Names Blindly.ipynb => 13 - Don't Use args and kwargs Names Blindly.ipynb} (100%) rename Part 1/Section 10 - Extras/{11 -command line arguments => 14 -command line arguments}/example1.py (100%) rename Part 1/Section 10 - Extras/{11 -command line arguments => 14 -command line arguments}/example10.py (100%) rename Part 1/Section 10 - Extras/{11 -command line arguments => 14 -command line arguments}/example2.py (100%) rename Part 1/Section 10 - Extras/{11 -command line arguments => 14 -command line arguments}/example3.py (100%) rename Part 1/Section 10 - Extras/{11 -command line arguments => 14 -command line arguments}/example4.py (100%) rename Part 1/Section 10 - Extras/{11 -command line arguments => 14 -command line arguments}/example5.py (100%) rename Part 1/Section 10 - Extras/{11 -command line arguments => 14 -command line arguments}/example6.py (100%) rename Part 1/Section 10 - Extras/{11 -command line arguments => 14 -command line arguments}/example7.py (100%) rename Part 1/Section 10 - Extras/{11 -command line arguments => 14 -command line arguments}/example8.py (100%) rename Part 1/Section 10 - Extras/{11 -command line arguments => 14 -command line arguments}/example9.py (100%) rename Part 1/Section 10 - Extras/{12 - Sentinel Values for Parameter Defaults.ipynb => 15 - Sentinel Values for Parameter Defaults.ipynb} (100%) rename Part 1/Section 10 - Extras/{13 - Simulating a simple Switch in Python.ipynb => 16 - Simulating a simple Switch in Python.ipynb} (100%) create mode 100644 Part 2/Section 14 - Extras/01 - Relevant Python 3.8 Changes.ipynb create mode 100644 Part 2/Section 14 - Extras/02 - Relevant Python 3.9 Changes.ipynb create mode 100644 Part 2/Section 14 - Extras/03 - Relevant Python 3.10 Changes.ipynb create mode 100644 Part 3/Section 11 - Extras/01 - Relevant Python 3.8 Changes.ipynb create mode 100644 Part 3/Section 11 - Extras/02 - Relevant Python 3.9 Changes.ipynb create mode 100644 Part 3/Section 11 - Extras/03 - Relevant Python 3.10 Changes.ipynb rename Part 3/Section 11 - Extras/{01 - MappingProxyType.ipynb => 04 - MappingProxyType.ipynb} (100%) create mode 100644 Part 4/Section 15 - Extras/01 - Relevant Python 3.8 Changes.ipynb create mode 100644 Part 4/Section 15 - Extras/02 - Relevant Python 3.9 Changes.ipynb create mode 100644 Part 4/Section 15 - Extras/03 - Relevant Python 3.10 Changes.ipynb diff --git a/Part 1/Section 10 - Extras/14 - Relevant Python 3.8 Changes.ipynb b/Part 1/Section 10 - Extras/01 - Relevant Python 3.8 Changes.ipynb similarity index 100% rename from Part 1/Section 10 - Extras/14 - Relevant Python 3.8 Changes.ipynb rename to Part 1/Section 10 - Extras/01 - Relevant Python 3.8 Changes.ipynb diff --git a/Part 1/Section 10 - Extras/15 - Relevant Python 3.9 Changes.ipynb b/Part 1/Section 10 - Extras/02 - Relevant Python 3.9 Changes.ipynb similarity index 100% rename from Part 1/Section 10 - Extras/15 - Relevant Python 3.9 Changes.ipynb rename to Part 1/Section 10 - Extras/02 - Relevant Python 3.9 Changes.ipynb diff --git a/Part 1/Section 10 - Extras/16 - Relevant Python 3.10 Changes.ipynb b/Part 1/Section 10 - Extras/03 - Relevant Python 3.10 Changes.ipynb similarity index 100% rename from Part 1/Section 10 - Extras/16 - Relevant Python 3.10 Changes.ipynb rename to Part 1/Section 10 - Extras/03 - Relevant Python 3.10 Changes.ipynb diff --git a/Part 1/Section 10 - Extras/01 - Python 3.6 Highlights.ipynb b/Part 1/Section 10 - Extras/04 - Python 3.6 Highlights.ipynb similarity index 100% rename from Part 1/Section 10 - Extras/01 - Python 3.6 Highlights.ipynb rename to Part 1/Section 10 - Extras/04 - Python 3.6 Highlights.ipynb diff --git a/Part 1/Section 10 - Extras/02 - Python 3.6 - Dictionary Ordering.ipynb b/Part 1/Section 10 - Extras/05 - Python 3.6 - Dictionary Ordering.ipynb similarity index 100% rename from Part 1/Section 10 - Extras/02 - Python 3.6 - Dictionary Ordering.ipynb rename to Part 1/Section 10 - Extras/05 - Python 3.6 - Dictionary Ordering.ipynb diff --git a/Part 1/Section 10 - Extras/03 - Python 3.6 - Preserved Order of kwargs - Named Tuple Application.ipynb b/Part 1/Section 10 - Extras/06 - Python 3.6 - Preserved Order of kwargs - Named Tuple Application.ipynb similarity index 100% rename from Part 1/Section 10 - Extras/03 - Python 3.6 - Preserved Order of kwargs - Named Tuple Application.ipynb rename to Part 1/Section 10 - Extras/06 - Python 3.6 - Preserved Order of kwargs - Named Tuple Application.ipynb diff --git a/Part 1/Section 10 - Extras/04 - Python 3.6 - Underscores and Numeric Literals.ipynb b/Part 1/Section 10 - Extras/07 - Python 3.6 - Underscores and Numeric Literals.ipynb similarity index 100% rename from Part 1/Section 10 - Extras/04 - Python 3.6 - Underscores and Numeric Literals.ipynb rename to Part 1/Section 10 - Extras/07 - Python 3.6 - Underscores and Numeric Literals.ipynb diff --git a/Part 1/Section 10 - Extras/05 - Python 3.6 - f-Strings.ipynb b/Part 1/Section 10 - Extras/08 - Python 3.6 - f-Strings.ipynb similarity index 100% rename from Part 1/Section 10 - Extras/05 - Python 3.6 - f-Strings.ipynb rename to Part 1/Section 10 - Extras/08 - Python 3.6 - f-Strings.ipynb diff --git a/Part 1/Section 10 - Extras/06 - Random Seeds.ipynb b/Part 1/Section 10 - Extras/09 - Random Seeds.ipynb similarity index 100% rename from Part 1/Section 10 - Extras/06 - Random Seeds.ipynb rename to Part 1/Section 10 - Extras/09 - Random Seeds.ipynb diff --git a/Part 1/Section 10 - Extras/07 - Random Choices.ipynb b/Part 1/Section 10 - Extras/10 - Random Choices.ipynb similarity index 100% rename from Part 1/Section 10 - Extras/07 - Random Choices.ipynb rename to Part 1/Section 10 - Extras/10 - Random Choices.ipynb diff --git a/Part 1/Section 10 - Extras/08 - Random Samples.ipynb b/Part 1/Section 10 - Extras/11 - Random Samples.ipynb similarity index 100% rename from Part 1/Section 10 - Extras/08 - Random Samples.ipynb rename to Part 1/Section 10 - Extras/11 - Random Samples.ipynb diff --git a/Part 1/Section 10 - Extras/09 - Timing code using timeit.ipynb b/Part 1/Section 10 - Extras/12 - Timing code using timeit.ipynb similarity index 100% rename from Part 1/Section 10 - Extras/09 - Timing code using timeit.ipynb rename to Part 1/Section 10 - Extras/12 - Timing code using timeit.ipynb diff --git a/Part 1/Section 10 - Extras/10 - Don't Use args and kwargs Names Blindly.ipynb b/Part 1/Section 10 - Extras/13 - Don't Use args and kwargs Names Blindly.ipynb similarity index 100% rename from Part 1/Section 10 - Extras/10 - Don't Use args and kwargs Names Blindly.ipynb rename to Part 1/Section 10 - Extras/13 - Don't Use args and kwargs Names Blindly.ipynb diff --git a/Part 1/Section 10 - Extras/11 -command line arguments/example1.py b/Part 1/Section 10 - Extras/14 -command line arguments/example1.py similarity index 100% rename from Part 1/Section 10 - Extras/11 -command line arguments/example1.py rename to Part 1/Section 10 - Extras/14 -command line arguments/example1.py diff --git a/Part 1/Section 10 - Extras/11 -command line arguments/example10.py b/Part 1/Section 10 - Extras/14 -command line arguments/example10.py similarity index 100% rename from Part 1/Section 10 - Extras/11 -command line arguments/example10.py rename to Part 1/Section 10 - Extras/14 -command line arguments/example10.py diff --git a/Part 1/Section 10 - Extras/11 -command line arguments/example2.py b/Part 1/Section 10 - Extras/14 -command line arguments/example2.py similarity index 100% rename from Part 1/Section 10 - Extras/11 -command line arguments/example2.py rename to Part 1/Section 10 - Extras/14 -command line arguments/example2.py diff --git a/Part 1/Section 10 - Extras/11 -command line arguments/example3.py b/Part 1/Section 10 - Extras/14 -command line arguments/example3.py similarity index 100% rename from Part 1/Section 10 - Extras/11 -command line arguments/example3.py rename to Part 1/Section 10 - Extras/14 -command line arguments/example3.py diff --git a/Part 1/Section 10 - Extras/11 -command line arguments/example4.py b/Part 1/Section 10 - Extras/14 -command line arguments/example4.py similarity index 100% rename from Part 1/Section 10 - Extras/11 -command line arguments/example4.py rename to Part 1/Section 10 - Extras/14 -command line arguments/example4.py diff --git a/Part 1/Section 10 - Extras/11 -command line arguments/example5.py b/Part 1/Section 10 - Extras/14 -command line arguments/example5.py similarity index 100% rename from Part 1/Section 10 - Extras/11 -command line arguments/example5.py rename to Part 1/Section 10 - Extras/14 -command line arguments/example5.py diff --git a/Part 1/Section 10 - Extras/11 -command line arguments/example6.py b/Part 1/Section 10 - Extras/14 -command line arguments/example6.py similarity index 100% rename from Part 1/Section 10 - Extras/11 -command line arguments/example6.py rename to Part 1/Section 10 - Extras/14 -command line arguments/example6.py diff --git a/Part 1/Section 10 - Extras/11 -command line arguments/example7.py b/Part 1/Section 10 - Extras/14 -command line arguments/example7.py similarity index 100% rename from Part 1/Section 10 - Extras/11 -command line arguments/example7.py rename to Part 1/Section 10 - Extras/14 -command line arguments/example7.py diff --git a/Part 1/Section 10 - Extras/11 -command line arguments/example8.py b/Part 1/Section 10 - Extras/14 -command line arguments/example8.py similarity index 100% rename from Part 1/Section 10 - Extras/11 -command line arguments/example8.py rename to Part 1/Section 10 - Extras/14 -command line arguments/example8.py diff --git a/Part 1/Section 10 - Extras/11 -command line arguments/example9.py b/Part 1/Section 10 - Extras/14 -command line arguments/example9.py similarity index 100% rename from Part 1/Section 10 - Extras/11 -command line arguments/example9.py rename to Part 1/Section 10 - Extras/14 -command line arguments/example9.py diff --git a/Part 1/Section 10 - Extras/12 - Sentinel Values for Parameter Defaults.ipynb b/Part 1/Section 10 - Extras/15 - Sentinel Values for Parameter Defaults.ipynb similarity index 100% rename from Part 1/Section 10 - Extras/12 - Sentinel Values for Parameter Defaults.ipynb rename to Part 1/Section 10 - Extras/15 - Sentinel Values for Parameter Defaults.ipynb diff --git a/Part 1/Section 10 - Extras/13 - Simulating a simple Switch in Python.ipynb b/Part 1/Section 10 - Extras/16 - Simulating a simple Switch in Python.ipynb similarity index 100% rename from Part 1/Section 10 - Extras/13 - Simulating a simple Switch in Python.ipynb rename to Part 1/Section 10 - Extras/16 - Simulating a simple Switch in Python.ipynb diff --git a/Part 2/Section 14 - Extras/01 - Relevant Python 3.8 Changes.ipynb b/Part 2/Section 14 - Extras/01 - Relevant Python 3.8 Changes.ipynb new file mode 100644 index 00000000..699f8626 --- /dev/null +++ b/Part 2/Section 14 - Extras/01 - Relevant Python 3.8 Changes.ipynb @@ -0,0 +1,1476 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "c314393b", + "metadata": {}, + "source": [ + "### Relevant Python 3.8 Changes" + ] + }, + { + "cell_type": "markdown", + "id": "cbe45497", + "metadata": {}, + "source": [ + "The release of Python 3.8 has brought some new features.\n", + "\n", + "This is a summary of the ones _I_ deemed relevant to this course, and does **not** include all the changes!\n", + "\n", + "For full release details, see [here](https://docs.python.org/3/whatsnew/3.8.html)" + ] + }, + { + "cell_type": "markdown", + "id": "7089277d", + "metadata": {}, + "source": [ + "#### Positional Only Parameters" + ] + }, + { + "cell_type": "markdown", + "id": "61154c87", + "metadata": {}, + "source": [ + "It is now possible to define **positional-only** parameters for Python functions." + ] + }, + { + "cell_type": "markdown", + "id": "63037bda", + "metadata": {}, + "source": [ + "As we saw earlier in this course, when you define positional parameters in a function:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "13c1ba59", + "metadata": {}, + "outputs": [], + "source": [ + "def my_func(a, b):\n", + " return a + b" + ] + }, + { + "cell_type": "markdown", + "id": "a4928079", + "metadata": {}, + "source": [ + "the user is free to pass arguments either positionally:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "0bb6fea5", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "my_func(1, 2)" + ] + }, + { + "cell_type": "markdown", + "id": "be1cfa90", + "metadata": {}, + "source": [ + "or, as named arguments:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "cb04e54f", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "my_func(b=2, a=1)" + ] + }, + { + "cell_type": "markdown", + "id": "617077dd", + "metadata": {}, + "source": [ + "Some functions in Python's built-ins ared defined in such a way that certain parameters can **only** be passed positionally, for example the `print` function:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "77c6fa8a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Help on built-in function print in module builtins:\n", + "\n", + "print(...)\n", + " print(value, ..., sep=' ', end='\\n', file=sys.stdout, flush=False)\n", + " \n", + " Prints the values to a stream, or to sys.stdout by default.\n", + " Optional keyword arguments:\n", + " file: a file-like object (stream); defaults to the current sys.stdout.\n", + " sep: string inserted between values, default a space.\n", + " end: string appended after the last value, default a newline.\n", + " flush: whether to forcibly flush the stream.\n", + "\n" + ] + } + ], + "source": [ + "help(print)" + ] + }, + { + "cell_type": "markdown", + "id": "248c2581", + "metadata": {}, + "source": [ + "That `value` cannot be passed by name:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "529c5dac", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "'value' is an invalid keyword argument for print()\n" + ] + } + ], + "source": [ + "try:\n", + " print(value=\"hello\")\n", + "except TypeError as ex:\n", + " print(ex)" + ] + }, + { + "cell_type": "markdown", + "id": "f4baed67", + "metadata": {}, + "source": [ + "Instead, the parameter **must** be passed positionally:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "8c147557", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "hello\n" + ] + } + ], + "source": [ + "print(\"hello\")" + ] + }, + { + "cell_type": "markdown", + "id": "d2840374", + "metadata": {}, + "source": [ + "Until Python 3.8, it was not possible to reproduce such behavior with user-defined functions." + ] + }, + { + "cell_type": "markdown", + "id": "158674a9", + "metadata": {}, + "source": [ + "Now you can, by using the slash character(`/`). Parameters defined **before** the `/` become **positional-only** parameters:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "aeebd0f8", + "metadata": {}, + "outputs": [], + "source": [ + "def my_func(a, b, /):\n", + " return a + b" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "858727b6", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "my_func(1, 2)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "4f73c741", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "my_func() got some positional-only arguments passed as keyword arguments: 'a, b'\n" + ] + } + ], + "source": [ + "try:\n", + " my_func(a=1, b=2)\n", + "except TypeError as ex:\n", + " print(ex)" + ] + }, + { + "cell_type": "markdown", + "id": "f5967700", + "metadata": {}, + "source": [ + "You can of course mix this along with the special parameters `*` and `**`:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "d17254b3", + "metadata": {}, + "outputs": [], + "source": [ + "def my_func(a, b, /, *, c):\n", + " print(a + b + c)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "b35d884e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "13\n" + ] + } + ], + "source": [ + "my_func(1, 2, c=10)" + ] + }, + { + "cell_type": "markdown", + "id": "573aca30", + "metadata": {}, + "source": [ + "#### f-string Enhancements" + ] + }, + { + "cell_type": "markdown", + "id": "89ee51f9", + "metadata": {}, + "source": [ + "Often we use f-strings to interpolate the name of a variable and it's value:" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "f79a630b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "a=hello, b=world\n" + ] + } + ], + "source": [ + "a, b = \"hello\", \"world\"\n", + "print(f\"a={a}, b={b}\")" + ] + }, + { + "cell_type": "markdown", + "id": "24747607", + "metadata": {}, + "source": [ + "Python 3.8 provides a shortcut way of doing the same thing:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "3ae5c0c4", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "a='hello', b='world'\n" + ] + } + ], + "source": [ + "print(f\"{a=}, {b=}\")" + ] + }, + { + "cell_type": "markdown", + "id": "141558b4", + "metadata": {}, + "source": [ + "You can even use [format specifiers](https://docs.python.org/3/library/string.html#formatspec)\n", + "to further customize the output:" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "e75716ac", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "a=hello, b=world\n" + ] + } + ], + "source": [ + "print(f\"{a=:s}, {b=:s}\")" + ] + }, + { + "cell_type": "markdown", + "id": "4dcdc5d0", + "metadata": {}, + "source": [ + "Or when dealing with other types:" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "362bf6b5", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import datetime\n", + "from math import pi" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "c91b7536", + "metadata": {}, + "outputs": [], + "source": [ + "d = datetime.utcnow()\n", + "e = pi" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "545c77a2", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "d=datetime.datetime(2022, 3, 20, 6, 1, 13, 990493), e=3.141592653589793\n" + ] + } + ], + "source": [ + "print(f\"{d=}, {e=}\")" + ] + }, + { + "cell_type": "markdown", + "id": "4637ecd5", + "metadata": {}, + "source": [ + "And applying some format specifiers:" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "75c4052b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "d=2022-03-20 06:01:13, e=3.142\n" + ] + } + ], + "source": [ + "print(f\"{d=:%Y-%m-%d %H:%M:%S}, {e=:.3f}\")" + ] + }, + { + "cell_type": "markdown", + "id": "076773ed", + "metadata": {}, + "source": [ + "It will even display the text of an expression if you use one in your f-string:" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "0ce46ace", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1 + 2=3, ' '.join(sentence)='Python rocks!'\n" + ] + } + ], + "source": [ + "sentence = [\"Python\", \"rocks!\"]\n", + "print(f\"{1 + 2=}, {' '.join(sentence)=}\")" + ] + }, + { + "cell_type": "markdown", + "id": "9e3783ea", + "metadata": {}, + "source": [ + "#### The `as_integer_ratio()` Method" + ] + }, + { + "cell_type": "markdown", + "id": "15ccd5ad", + "metadata": {}, + "source": [ + "The types `bool`, `int` and `Fraction` now all implement an `as_integer_ratio()` method which returns a tuple consisting of the numerator and denominator. Remember that `Decimal` and `float` already implement the same method." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "8313fc80", + "metadata": {}, + "outputs": [], + "source": [ + "from fractions import Fraction" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "7a1ae84a", + "metadata": {}, + "outputs": [], + "source": [ + "f = Fraction(2, 3)" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "a94fc3ed", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(2, 3)" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "f.as_integer_ratio()" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "f32942ec", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(12, 1)" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a = 12\n", + "a.as_integer_ratio()" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "06980a8c", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(1, 1)" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "flag = True\n", + "flag.as_integer_ratio()" + ] + }, + { + "cell_type": "markdown", + "id": "a3091e0e", + "metadata": {}, + "source": [ + "The advantage of this is mainly for polymorphism (or duck-typing), where you can now use `as_integer_ratio` irrespective of whether the variable is a `bool`, an `int`, a `float`, a `Decimal` or a `Fraction`." + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "8a362bf8", + "metadata": {}, + "outputs": [], + "source": [ + "from decimal import Decimal" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "ab4a78aa", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(33, 100)" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Decimal(\"0.33\").as_integer_ratio()" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "5ee29a47", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(7070651414971679, 2251799813685248)" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(3.14).as_integer_ratio()" + ] + }, + { + "cell_type": "markdown", + "id": "df1e85de", + "metadata": {}, + "source": [ + "#### The `lru_cache` decorator" + ] + }, + { + "cell_type": "markdown", + "id": "226efb47", + "metadata": {}, + "source": [ + "As we saw in this course, we can use the `lru_cache` decorator to appky an LRU cache to our functions:" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "20501526", + "metadata": {}, + "outputs": [], + "source": [ + "from functools import lru_cache" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "07d699f0", + "metadata": {}, + "outputs": [], + "source": [ + "@lru_cache(maxsize=3)\n", + "def fib(n):\n", + " if n <=2 :\n", + " return 1\n", + " else:\n", + " return fib(n-1) + fib(n-2)" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "45a0cd5a", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "354224848179261915075" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "fib(100)" + ] + }, + { + "cell_type": "markdown", + "id": "fea747d4", + "metadata": {}, + "source": [ + "If we don't specify `maxsize`, it will default to `128`:" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "id": "447be276", + "metadata": {}, + "outputs": [], + "source": [ + "@lru_cache()\n", + "def fib(n):\n", + " if n <=2 :\n", + " return 1\n", + " else:\n", + " return fib(n-1) + fib(n-2)" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "id": "53fee4fd", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "354224848179261915075" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "fib(100)" + ] + }, + { + "cell_type": "markdown", + "id": "5cf8e33a", + "metadata": {}, + "source": [ + "The change made to this decorator in Python 3.8 allows us not to use those empty parentheses:" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "id": "4185658b", + "metadata": {}, + "outputs": [], + "source": [ + "@lru_cache\n", + "def fib(n):\n", + " if n <=2 :\n", + " return 1\n", + " else:\n", + " return fib(n-1) + fib(n-2)" + ] + }, + { + "cell_type": "markdown", + "id": "76a9aed5", + "metadata": {}, + "source": [ + "#### `math` Module" + ] + }, + { + "cell_type": "markdown", + "id": "0af410c0", + "metadata": {}, + "source": [ + "Many examples I use throughout this course calculate the Euclidean distance between two points:" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "id": "b511addc", + "metadata": {}, + "outputs": [], + "source": [ + "import math" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "id": "0b66c006", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1.4142135623730951\n" + ] + } + ], + "source": [ + "a = (0, 0)\n", + "b = (1, 1)\n", + "\n", + "dist = math.sqrt((b[0] - a[1]) ** 2 + (b[1] - a[1]) ** 2)\n", + "print(dist)" + ] + }, + { + "cell_type": "markdown", + "id": "d276589d", + "metadata": {}, + "source": [ + "Now, it's much easier using the `dist()` function the `math` module:" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "id": "1f774757", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "1.4142135623730951" + ] + }, + "execution_count": 36, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "math.dist(a, b)" + ] + }, + { + "cell_type": "markdown", + "id": "13afb1f1", + "metadata": {}, + "source": [ + "#### The `namedtuple` Implementation" + ] + }, + { + "cell_type": "markdown", + "id": "a22a839e", + "metadata": {}, + "source": [ + "Actually these changes were added to Python 3.7, but since I don't have a separate lecture for Python 3.7 changes (most did not apply to this course), here it is." + ] + }, + { + "cell_type": "markdown", + "id": "1a579fbb", + "metadata": {}, + "source": [ + "The `_source` attribute was **removed**. There quite a discussion on this, and the the core dev who implemented and supported this essentially gave up trying to keep this in - it was deemed to cause too much \"overhead\". So, sadly (wearing my teacher's hat), it is gone. It is no more. It's not pining, it's just dead. :-)" + ] + }, + { + "cell_type": "markdown", + "id": "d5e3bfd0", + "metadata": {}, + "source": [ + "The method I showed you for defining defaults for named tuples still works, and could still be used, but Python 3.7 added the `defaults` parameter to the named tuple definition." + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "id": "3538a2c2", + "metadata": {}, + "outputs": [], + "source": [ + "from collections import namedtuple" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "id": "024bcc51", + "metadata": {}, + "outputs": [], + "source": [ + "NT = namedtuple(\"NT\", \"a b c\", defaults=(10, 20, 30))" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "id": "9dd8a877", + "metadata": {}, + "outputs": [], + "source": [ + "nt = NT()" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "id": "db7f47e7", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "NT(a=10, b=20, c=30)" + ] + }, + "execution_count": 40, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nt" + ] + }, + { + "cell_type": "markdown", + "id": "fb0bc811", + "metadata": {}, + "source": [ + "You don't have to specify defaults for everything, but if you do not, be aware that defaults will be applied from **right** to **left**. Which makes sense given that in Python non-defaulted parameters must be defined **before** defaulted parameters." + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "id": "8f6ad2ca", + "metadata": {}, + "outputs": [], + "source": [ + "NT = namedtuple(\"NT\", \"a b c\", defaults = (20, 30))" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "id": "bc9d5345", + "metadata": {}, + "outputs": [], + "source": [ + "nt = NT(10)" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "id": "eceeaec2", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "NT(a=10, b=20, c=30)" + ] + }, + "execution_count": 43, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nt" + ] + }, + { + "cell_type": "markdown", + "id": "64188b1c", + "metadata": {}, + "source": [ + "Note that with this way of specifying defaults you can easily define the same default for all items in the named tuple using the `*` operator:" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "id": "8b36d7c3", + "metadata": {}, + "outputs": [], + "source": [ + "NT = namedtuple(\"NT\", \"a b c d e f\", defaults=(\"xyz\",) * 6)" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "id": "1d448806", + "metadata": {}, + "outputs": [], + "source": [ + "nt = NT()" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "id": "b16758de", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "NT(a='xyz', b='xyz', c='xyz', d='xyz', e='xyz', f='xyz')" + ] + }, + "execution_count": 46, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nt" + ] + }, + { + "cell_type": "markdown", + "id": "bbcbaf53", + "metadata": {}, + "source": [ + "Just be careful if you use a **mutable** type to do this!!" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "id": "91b28470", + "metadata": {}, + "outputs": [], + "source": [ + "NT = namedtuple(\"NT\", \"a b c\", defaults = ([],) * 3)" + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "id": "cad11f5c", + "metadata": {}, + "outputs": [], + "source": [ + "nt = NT()" + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "id": "0cb7ceac", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "NT(a=[], b=[], c=[])" + ] + }, + "execution_count": 49, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nt" + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "id": "466d9520", + "metadata": {}, + "outputs": [], + "source": [ + "nt.a.append(10)" + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "id": "74826099", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[10]" + ] + }, + "execution_count": 51, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nt.a" + ] + }, + { + "cell_type": "markdown", + "id": "6d00b122", + "metadata": {}, + "source": [ + "But watch this!" + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "id": "4759e6f4", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "NT(a=[10], b=[10], c=[10])" + ] + }, + "execution_count": 52, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nt" + ] + }, + { + "cell_type": "markdown", + "id": "972c666e", + "metadata": {}, + "source": [ + "I hope you understand what happened here without me telling you!" + ] + }, + { + "cell_type": "markdown", + "id": "6590d73a", + "metadata": {}, + "source": [ + "The **same** list object was re-used 3 times in the defaults." + ] + }, + { + "cell_type": "markdown", + "id": "720e13bb", + "metadata": {}, + "source": [ + "You can easily recover your defaults using the `_field_defaults` method:" + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "id": "586498ae", + "metadata": {}, + "outputs": [], + "source": [ + "NT = namedtuple(\"NT\", \"a, b, c\", defaults=(1, 2, 3))" + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "id": "20f4012e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'a': 1, 'b': 2, 'c': 3}" + ] + }, + "execution_count": 54, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "NT._field_defaults" + ] + }, + { + "cell_type": "markdown", + "id": "70c3a717", + "metadata": {}, + "source": [ + "One change of note in Python 3.8, the `_as_dict()` method now returns a standard dictionary (key ordered in the same way as the named tuple). Prior to this version, it would return an `OrderedDict` since standard Python dictionaries did not guarantee any specific key order, but since they now do, there's no need to use the `DefaultDict` anymore." + ] + }, + { + "cell_type": "markdown", + "id": "ecb5b3a0", + "metadata": {}, + "source": [ + "#### Other Things" + ] + }, + { + "cell_type": "markdown", + "id": "9d8ec24b", + "metadata": {}, + "source": [ + "These are few other odds and ends that you might find of interest:" + ] + }, + { + "cell_type": "markdown", + "id": "3602bbab", + "metadata": {}, + "source": [ + "The built-in `reversed` function now works with dictionary views:" + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "id": "4f68e46b", + "metadata": {}, + "outputs": [], + "source": [ + "d = {'a': 1, 'b': 2}" + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "id": "83fc356d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['a', 'b']" + ] + }, + "execution_count": 56, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list(d.keys())" + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "id": "10648abb", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['b', 'a']" + ] + }, + "execution_count": 57, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list(reversed(d.keys()))" + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "id": "4a4885c3", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[2, 1]" + ] + }, + "execution_count": 58, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list(reversed(d.values()))" + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "id": "13785eb9", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[('b', 2), ('a', 1)]" + ] + }, + "execution_count": 59, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list(reversed(d.items()))" + ] + }, + { + "cell_type": "markdown", + "id": "e14d5c14", + "metadata": {}, + "source": [ + "The `continue` statement was not permitted in the `finally` clause of a loop, but is now supported." + ] + }, + { + "cell_type": "markdown", + "id": "fe507127", + "metadata": {}, + "source": [ + "Earlier in Part 1, we discussed string interning, as well as how a small selection of integers are essentially \"cached\" by Python and re-used whenever the literal is encountered. This meant we could use `is` instead of `==` in some cases, and that helped us get a clearer understanding of what's going on. **BUT**, as we also discussed, you should **not**, in practice, use `is` for comparing objects such as integers, strings, etc (usually we are more interested in whether is the same value, rather than the identical object) - the fact that this works is an implementation detail and not guaranteed to work the same way from one Python version to another.\n", + "\n", + "Although linters will usually catch those kinds of issues, not everyone uses a Python linter - so Python 3.8 will now emit a **warning** if you compare variables of certain types with literal constants!" + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "id": "e659956e", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "<>:2: SyntaxWarning: \"is\" with a literal. Did you mean \"==\"?\n", + "<>:2: SyntaxWarning: \"is\" with a literal. Did you mean \"==\"?\n", + "/var/folders/kg/20pf1n8j6pjg09bwxz6cgw0r0000gn/T/ipykernel_33116/3780910533.py:2: SyntaxWarning: \"is\" with a literal. Did you mean \"==\"?\n", + " a is 1\n" + ] + }, + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 60, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a = 1\n", + "a is 1" + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "id": "d970904d", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "<>:2: SyntaxWarning: \"is\" with a literal. Did you mean \"==\"?\n", + "<>:2: SyntaxWarning: \"is\" with a literal. Did you mean \"==\"?\n", + "/var/folders/kg/20pf1n8j6pjg09bwxz6cgw0r0000gn/T/ipykernel_33116/4272392880.py:2: SyntaxWarning: \"is\" with a literal. Did you mean \"==\"?\n", + " a is 'hello'\n" + ] + }, + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 61, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a = 'hello'\n", + "a is 'hello'" + ] + }, + { + "cell_type": "markdown", + "id": "01c01a49", + "metadata": {}, + "source": [ + "But we do not get a warning in a situation such as this:" + ] + }, + { + "cell_type": "code", + "execution_count": 62, + "id": "c0d535bc", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 62, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a = [1, 2, 3]\n", + "\n", + "a is [1, 2, 3]" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.10.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Part 2/Section 14 - Extras/02 - Relevant Python 3.9 Changes.ipynb b/Part 2/Section 14 - Extras/02 - Relevant Python 3.9 Changes.ipynb new file mode 100644 index 00000000..698dadce --- /dev/null +++ b/Part 2/Section 14 - Extras/02 - Relevant Python 3.9 Changes.ipynb @@ -0,0 +1,2343 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "c45f0998", + "metadata": {}, + "source": [ + "### Relevant Python 3.9 Changes" + ] + }, + { + "cell_type": "markdown", + "id": "2e965eba", + "metadata": {}, + "source": [ + "The release of Python 3.9 has brought some new features.\n", + "\n", + "This is a summary of the ones _I_ deemed relevant to this course, and does **not** include all the changes!\n", + "\n", + "For full release details, see [here](https://docs.python.org/3/whatsnew/3.9.html)" + ] + }, + { + "cell_type": "markdown", + "id": "39255227", + "metadata": {}, + "source": [ + "#### Time Zones" + ] + }, + { + "cell_type": "markdown", + "id": "26750adb", + "metadata": {}, + "source": [ + "We don't cover 3rd party libraries in this course, but if you've worked with Python in a production environment, you will likely have come across the dreaded timezone and Daylight Savings issues that plague datetimes!" + ] + }, + { + "cell_type": "markdown", + "id": "7cdfe8a5", + "metadata": {}, + "source": [ + "Most likely you will have resorted to using the `pytz` and `python-dateutil` libraries to help with that." + ] + }, + { + "cell_type": "markdown", + "id": "bc000883", + "metadata": {}, + "source": [ + "Now, Python 3.9 is proud to introduce the `zoneinfo` module to deal with timezones properly. About time too!" + ] + }, + { + "cell_type": "markdown", + "id": "c2d652dd", + "metadata": {}, + "source": [ + "For full info on this, refer to [PEP 615](https://peps.python.org/pep-0615/).\n", + "\n", + "And the Python [docs](https://docs.python.org/3.9/library/zoneinfo.html#module-zoneinfo).\n", + "\n", + "**Windows Users**: you will likely need to add a dependency on the `tzdata` [library](https://pypi.org/project/tzdata/) for the IANA time zone database. See [this note](https://docs.python.org/3.9/library/zoneinfo.html#data-sources)\n", + "\n", + "You should also take a look at this [presentation](https://pganssle-talks.github.io/chipy-nov-2020-zoneinfo/#/) by Paul Ganssle who wrote that module - very interesting read!" + ] + }, + { + "cell_type": "markdown", + "id": "80255944", + "metadata": {}, + "source": [ + "Let's look at how we might have handled timezone and DST using `pytz` and `dateutil`, and contrast that to how we can use the new `zoneinfo` module instead." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "ec129288", + "metadata": {}, + "outputs": [], + "source": [ + "import zoneinfo\n", + "from datetime import datetime, timezone\n", + "from zoneinfo import ZoneInfo\n", + "\n", + "import dateutil\n", + "import pytz" + ] + }, + { + "cell_type": "markdown", + "id": "beadc4e2", + "metadata": {}, + "source": [ + "Let's list out all the defined time zones:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "d0058e73", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Africa/Abidjan\n", + "Africa/Accra\n", + "Africa/Addis_Ababa\n", + "Africa/Algiers\n", + "Africa/Asmara\n", + "Africa/Asmera\n", + "Africa/Bamako\n", + "Africa/Bangui\n", + "Africa/Banjul\n", + "Africa/Bissau\n", + "Africa/Blantyre\n", + "Africa/Brazzaville\n", + "Africa/Bujumbura\n", + "Africa/Cairo\n", + "Africa/Casablanca\n", + "Africa/Ceuta\n", + "Africa/Conakry\n", + "Africa/Dakar\n", + "Africa/Dar_es_Salaam\n", + "Africa/Djibouti\n", + "Africa/Douala\n", + "Africa/El_Aaiun\n", + "Africa/Freetown\n", + "Africa/Gaborone\n", + "Africa/Harare\n", + "Africa/Johannesburg\n", + "Africa/Juba\n", + "Africa/Kampala\n", + "Africa/Khartoum\n", + "Africa/Kigali\n", + "Africa/Kinshasa\n", + "Africa/Lagos\n", + "Africa/Libreville\n", + "Africa/Lome\n", + "Africa/Luanda\n", + "Africa/Lubumbashi\n", + "Africa/Lusaka\n", + "Africa/Malabo\n", + "Africa/Maputo\n", + "Africa/Maseru\n", + "Africa/Mbabane\n", + "Africa/Mogadishu\n", + "Africa/Monrovia\n", + "Africa/Nairobi\n", + "Africa/Ndjamena\n", + "Africa/Niamey\n", + "Africa/Nouakchott\n", + "Africa/Ouagadougou\n", + "Africa/Porto-Novo\n", + "Africa/Sao_Tome\n", + "Africa/Timbuktu\n", + "Africa/Tripoli\n", + "Africa/Tunis\n", + "Africa/Windhoek\n", + "America/Adak\n", + "America/Anchorage\n", + "America/Anguilla\n", + "America/Antigua\n", + "America/Araguaina\n", + "America/Argentina/Buenos_Aires\n", + "America/Argentina/Catamarca\n", + "America/Argentina/ComodRivadavia\n", + "America/Argentina/Cordoba\n", + "America/Argentina/Jujuy\n", + "America/Argentina/La_Rioja\n", + "America/Argentina/Mendoza\n", + "America/Argentina/Rio_Gallegos\n", + "America/Argentina/Salta\n", + "America/Argentina/San_Juan\n", + "America/Argentina/San_Luis\n", + "America/Argentina/Tucuman\n", + "America/Argentina/Ushuaia\n", + "America/Aruba\n", + "America/Asuncion\n", + "America/Atikokan\n", + "America/Atka\n", + "America/Bahia\n", + "America/Bahia_Banderas\n", + "America/Barbados\n", + "America/Belem\n", + "America/Belize\n", + "America/Blanc-Sablon\n", + "America/Boa_Vista\n", + "America/Bogota\n", + "America/Boise\n", + "America/Buenos_Aires\n", + "America/Cambridge_Bay\n", + "America/Campo_Grande\n", + "America/Cancun\n", + "America/Caracas\n", + "America/Catamarca\n", + "America/Cayenne\n", + "America/Cayman\n", + "America/Chicago\n", + "America/Chihuahua\n", + "America/Coral_Harbour\n", + "America/Cordoba\n", + "America/Costa_Rica\n", + "America/Creston\n", + "America/Cuiaba\n", + "America/Curacao\n", + "America/Danmarkshavn\n", + "America/Dawson\n", + "America/Dawson_Creek\n", + "America/Denver\n", + "America/Detroit\n", + "America/Dominica\n", + "America/Edmonton\n", + "America/Eirunepe\n", + "America/El_Salvador\n", + "America/Ensenada\n", + "America/Fort_Nelson\n", + "America/Fort_Wayne\n", + "America/Fortaleza\n", + "America/Glace_Bay\n", + "America/Godthab\n", + "America/Goose_Bay\n", + "America/Grand_Turk\n", + "America/Grenada\n", + "America/Guadeloupe\n", + "America/Guatemala\n", + "America/Guayaquil\n", + "America/Guyana\n", + "America/Halifax\n", + "America/Havana\n", + "America/Hermosillo\n", + "America/Indiana/Indianapolis\n", + "America/Indiana/Knox\n", + "America/Indiana/Marengo\n", + "America/Indiana/Petersburg\n", + "America/Indiana/Tell_City\n", + "America/Indiana/Vevay\n", + "America/Indiana/Vincennes\n", + "America/Indiana/Winamac\n", + "America/Indianapolis\n", + "America/Inuvik\n", + "America/Iqaluit\n", + "America/Jamaica\n", + "America/Jujuy\n", + "America/Juneau\n", + "America/Kentucky/Louisville\n", + "America/Kentucky/Monticello\n", + "America/Knox_IN\n", + "America/Kralendijk\n", + "America/La_Paz\n", + "America/Lima\n", + "America/Los_Angeles\n", + "America/Louisville\n", + "America/Lower_Princes\n", + "America/Maceio\n", + "America/Managua\n", + "America/Manaus\n", + "America/Marigot\n", + "America/Martinique\n", + "America/Matamoros\n", + "America/Mazatlan\n", + "America/Mendoza\n", + "America/Menominee\n", + "America/Merida\n", + "America/Metlakatla\n", + "America/Mexico_City\n", + "America/Miquelon\n", + "America/Moncton\n", + "America/Monterrey\n", + "America/Montevideo\n", + "America/Montreal\n", + "America/Montserrat\n", + "America/Nassau\n", + "America/New_York\n", + "America/Nipigon\n", + "America/Nome\n", + "America/Noronha\n", + "America/North_Dakota/Beulah\n", + "America/North_Dakota/Center\n", + "America/North_Dakota/New_Salem\n", + "America/Nuuk\n", + "America/Ojinaga\n", + "America/Panama\n", + "America/Pangnirtung\n", + "America/Paramaribo\n", + "America/Phoenix\n", + "America/Port-au-Prince\n", + "America/Port_of_Spain\n", + "America/Porto_Acre\n", + "America/Porto_Velho\n", + "America/Puerto_Rico\n", + "America/Punta_Arenas\n", + "America/Rainy_River\n", + "America/Rankin_Inlet\n", + "America/Recife\n", + "America/Regina\n", + "America/Resolute\n", + "America/Rio_Branco\n", + "America/Rosario\n", + "America/Santa_Isabel\n", + "America/Santarem\n", + "America/Santiago\n", + "America/Santo_Domingo\n", + "America/Sao_Paulo\n", + "America/Scoresbysund\n", + "America/Shiprock\n", + "America/Sitka\n", + "America/St_Barthelemy\n", + "America/St_Johns\n", + "America/St_Kitts\n", + "America/St_Lucia\n", + "America/St_Thomas\n", + "America/St_Vincent\n", + "America/Swift_Current\n", + "America/Tegucigalpa\n", + "America/Thule\n", + "America/Thunder_Bay\n", + "America/Tijuana\n", + "America/Toronto\n", + "America/Tortola\n", + "America/Vancouver\n", + "America/Virgin\n", + "America/Whitehorse\n", + "America/Winnipeg\n", + "America/Yakutat\n", + "America/Yellowknife\n", + "Antarctica/Casey\n", + "Antarctica/Davis\n", + "Antarctica/DumontDUrville\n", + "Antarctica/Macquarie\n", + "Antarctica/Mawson\n", + "Antarctica/McMurdo\n", + "Antarctica/Palmer\n", + "Antarctica/Rothera\n", + "Antarctica/South_Pole\n", + "Antarctica/Syowa\n", + "Antarctica/Troll\n", + "Antarctica/Vostok\n", + "Arctic/Longyearbyen\n", + "Asia/Aden\n", + "Asia/Almaty\n", + "Asia/Amman\n", + "Asia/Anadyr\n", + "Asia/Aqtau\n", + "Asia/Aqtobe\n", + "Asia/Ashgabat\n", + "Asia/Ashkhabad\n", + "Asia/Atyrau\n", + "Asia/Baghdad\n", + "Asia/Bahrain\n", + "Asia/Baku\n", + "Asia/Bangkok\n", + "Asia/Barnaul\n", + "Asia/Beirut\n", + "Asia/Bishkek\n", + "Asia/Brunei\n", + "Asia/Calcutta\n", + "Asia/Chita\n", + "Asia/Choibalsan\n", + "Asia/Chongqing\n", + "Asia/Chungking\n", + "Asia/Colombo\n", + "Asia/Dacca\n", + "Asia/Damascus\n", + "Asia/Dhaka\n", + "Asia/Dili\n", + "Asia/Dubai\n", + "Asia/Dushanbe\n", + "Asia/Famagusta\n", + "Asia/Gaza\n", + "Asia/Harbin\n", + "Asia/Hebron\n", + "Asia/Ho_Chi_Minh\n", + "Asia/Hong_Kong\n", + "Asia/Hovd\n", + "Asia/Irkutsk\n", + "Asia/Istanbul\n", + "Asia/Jakarta\n", + "Asia/Jayapura\n", + "Asia/Jerusalem\n", + "Asia/Kabul\n", + "Asia/Kamchatka\n", + "Asia/Karachi\n", + "Asia/Kashgar\n", + "Asia/Kathmandu\n", + "Asia/Katmandu\n", + "Asia/Khandyga\n", + "Asia/Kolkata\n", + "Asia/Krasnoyarsk\n", + "Asia/Kuala_Lumpur\n", + "Asia/Kuching\n", + "Asia/Kuwait\n", + "Asia/Macao\n", + "Asia/Macau\n", + "Asia/Magadan\n", + "Asia/Makassar\n", + "Asia/Manila\n", + "Asia/Muscat\n", + "Asia/Nicosia\n", + "Asia/Novokuznetsk\n", + "Asia/Novosibirsk\n", + "Asia/Omsk\n", + "Asia/Oral\n", + "Asia/Phnom_Penh\n", + "Asia/Pontianak\n", + "Asia/Pyongyang\n", + "Asia/Qatar\n", + "Asia/Qostanay\n", + "Asia/Qyzylorda\n", + "Asia/Rangoon\n", + "Asia/Riyadh\n", + "Asia/Saigon\n", + "Asia/Sakhalin\n", + "Asia/Samarkand\n", + "Asia/Seoul\n", + "Asia/Shanghai\n", + "Asia/Singapore\n", + "Asia/Srednekolymsk\n", + "Asia/Taipei\n", + "Asia/Tashkent\n", + "Asia/Tbilisi\n", + "Asia/Tehran\n", + "Asia/Tel_Aviv\n", + "Asia/Thimbu\n", + "Asia/Thimphu\n", + "Asia/Tokyo\n", + "Asia/Tomsk\n", + "Asia/Ujung_Pandang\n", + "Asia/Ulaanbaatar\n", + "Asia/Ulan_Bator\n", + "Asia/Urumqi\n", + "Asia/Ust-Nera\n", + "Asia/Vientiane\n", + "Asia/Vladivostok\n", + "Asia/Yakutsk\n", + "Asia/Yangon\n", + "Asia/Yekaterinburg\n", + "Asia/Yerevan\n", + "Atlantic/Azores\n", + "Atlantic/Bermuda\n", + "Atlantic/Canary\n", + "Atlantic/Cape_Verde\n", + "Atlantic/Faeroe\n", + "Atlantic/Faroe\n", + "Atlantic/Jan_Mayen\n", + "Atlantic/Madeira\n", + "Atlantic/Reykjavik\n", + "Atlantic/South_Georgia\n", + "Atlantic/St_Helena\n", + "Atlantic/Stanley\n", + "Australia/ACT\n", + "Australia/Adelaide\n", + "Australia/Brisbane\n", + "Australia/Broken_Hill\n", + "Australia/Canberra\n", + "Australia/Currie\n", + "Australia/Darwin\n", + "Australia/Eucla\n", + "Australia/Hobart\n", + "Australia/LHI\n", + "Australia/Lindeman\n", + "Australia/Lord_Howe\n", + "Australia/Melbourne\n", + "Australia/NSW\n", + "Australia/North\n", + "Australia/Perth\n", + "Australia/Queensland\n", + "Australia/South\n", + "Australia/Sydney\n", + "Australia/Tasmania\n", + "Australia/Victoria\n", + "Australia/West\n", + "Australia/Yancowinna\n", + "Brazil/Acre\n", + "Brazil/DeNoronha\n", + "Brazil/East\n", + "Brazil/West\n", + "CET\n", + "CST6CDT\n", + "Canada/Atlantic\n", + "Canada/Central\n", + "Canada/Eastern\n", + "Canada/Mountain\n", + "Canada/Newfoundland\n", + "Canada/Pacific\n", + "Canada/Saskatchewan\n", + "Canada/Yukon\n", + "Chile/Continental\n", + "Chile/EasterIsland\n", + "Cuba\n", + "EET\n", + "EST\n", + "EST5EDT\n", + "Egypt\n", + "Eire\n", + "Etc/GMT\n", + "Etc/GMT+0\n", + "Etc/GMT+1\n", + "Etc/GMT+10\n", + "Etc/GMT+11\n", + "Etc/GMT+12\n", + "Etc/GMT+2\n", + "Etc/GMT+3\n", + "Etc/GMT+4\n", + "Etc/GMT+5\n", + "Etc/GMT+6\n", + "Etc/GMT+7\n", + "Etc/GMT+8\n", + "Etc/GMT+9\n", + "Etc/GMT-0\n", + "Etc/GMT-1\n", + "Etc/GMT-10\n", + "Etc/GMT-11\n", + "Etc/GMT-12\n", + "Etc/GMT-13\n", + "Etc/GMT-14\n", + "Etc/GMT-2\n", + "Etc/GMT-3\n", + "Etc/GMT-4\n", + "Etc/GMT-5\n", + "Etc/GMT-6\n", + "Etc/GMT-7\n", + "Etc/GMT-8\n", + "Etc/GMT-9\n", + "Etc/GMT0\n", + "Etc/Greenwich\n", + "Etc/UCT\n", + "Etc/UTC\n", + "Etc/Universal\n", + "Etc/Zulu\n", + "Europe/Amsterdam\n", + "Europe/Andorra\n", + "Europe/Astrakhan\n", + "Europe/Athens\n", + "Europe/Belfast\n", + "Europe/Belgrade\n", + "Europe/Berlin\n", + "Europe/Bratislava\n", + "Europe/Brussels\n", + "Europe/Bucharest\n", + "Europe/Budapest\n", + "Europe/Busingen\n", + "Europe/Chisinau\n", + "Europe/Copenhagen\n", + "Europe/Dublin\n", + "Europe/Gibraltar\n", + "Europe/Guernsey\n", + "Europe/Helsinki\n", + "Europe/Isle_of_Man\n", + "Europe/Istanbul\n", + "Europe/Jersey\n", + "Europe/Kaliningrad\n", + "Europe/Kiev\n", + "Europe/Kirov\n", + "Europe/Lisbon\n", + "Europe/Ljubljana\n", + "Europe/London\n", + "Europe/Luxembourg\n", + "Europe/Madrid\n", + "Europe/Malta\n", + "Europe/Mariehamn\n", + "Europe/Minsk\n", + "Europe/Monaco\n", + "Europe/Moscow\n", + "Europe/Nicosia\n", + "Europe/Oslo\n", + "Europe/Paris\n", + "Europe/Podgorica\n", + "Europe/Prague\n", + "Europe/Riga\n", + "Europe/Rome\n", + "Europe/Samara\n", + "Europe/San_Marino\n", + "Europe/Sarajevo\n", + "Europe/Saratov\n", + "Europe/Simferopol\n", + "Europe/Skopje\n", + "Europe/Sofia\n", + "Europe/Stockholm\n", + "Europe/Tallinn\n", + "Europe/Tirane\n", + "Europe/Tiraspol\n", + "Europe/Ulyanovsk\n", + "Europe/Uzhgorod\n", + "Europe/Vaduz\n", + "Europe/Vatican\n", + "Europe/Vienna\n", + "Europe/Vilnius\n", + "Europe/Volgograd\n", + "Europe/Warsaw\n", + "Europe/Zagreb\n", + "Europe/Zaporozhye\n", + "Europe/Zurich\n", + "GB\n", + "GB-Eire\n", + "GMT\n", + "GMT+0\n", + "GMT-0\n", + "GMT0\n", + "Greenwich\n", + "HST\n", + "Hongkong\n", + "Iceland\n", + "Indian/Antananarivo\n", + "Indian/Chagos\n", + "Indian/Christmas\n", + "Indian/Cocos\n", + "Indian/Comoro\n", + "Indian/Kerguelen\n", + "Indian/Mahe\n", + "Indian/Maldives\n", + "Indian/Mauritius\n", + "Indian/Mayotte\n", + "Indian/Reunion\n", + "Iran\n", + "Israel\n", + "Jamaica\n", + "Japan\n", + "Kwajalein\n", + "Libya\n", + "MET\n", + "MST\n", + "MST7MDT\n", + "Mexico/BajaNorte\n", + "Mexico/BajaSur\n", + "Mexico/General\n", + "NZ\n", + "NZ-CHAT\n", + "Navajo\n", + "PRC\n", + "PST8PDT\n", + "Pacific/Apia\n", + "Pacific/Auckland\n", + "Pacific/Bougainville\n", + "Pacific/Chatham\n", + "Pacific/Chuuk\n", + "Pacific/Easter\n", + "Pacific/Efate\n", + "Pacific/Enderbury\n", + "Pacific/Fakaofo\n", + "Pacific/Fiji\n", + "Pacific/Funafuti\n", + "Pacific/Galapagos\n", + "Pacific/Gambier\n", + "Pacific/Guadalcanal\n", + "Pacific/Guam\n", + "Pacific/Honolulu\n", + "Pacific/Johnston\n", + "Pacific/Kanton\n", + "Pacific/Kiritimati\n", + "Pacific/Kosrae\n", + "Pacific/Kwajalein\n", + "Pacific/Majuro\n", + "Pacific/Marquesas\n", + "Pacific/Midway\n", + "Pacific/Nauru\n", + "Pacific/Niue\n", + "Pacific/Norfolk\n", + "Pacific/Noumea\n", + "Pacific/Pago_Pago\n", + "Pacific/Palau\n", + "Pacific/Pitcairn\n", + "Pacific/Pohnpei\n", + "Pacific/Ponape\n", + "Pacific/Port_Moresby\n", + "Pacific/Rarotonga\n", + "Pacific/Saipan\n", + "Pacific/Samoa\n", + "Pacific/Tahiti\n", + "Pacific/Tarawa\n", + "Pacific/Tongatapu\n", + "Pacific/Truk\n", + "Pacific/Wake\n", + "Pacific/Wallis\n", + "Pacific/Yap\n", + "Poland\n", + "Portugal\n", + "ROC\n", + "ROK\n", + "Singapore\n", + "Turkey\n", + "UCT\n", + "US/Alaska\n", + "US/Aleutian\n", + "US/Arizona\n", + "US/Central\n", + "US/East-Indiana\n", + "US/Eastern\n", + "US/Hawaii\n", + "US/Indiana-Starke\n", + "US/Michigan\n", + "US/Mountain\n", + "US/Pacific\n", + "US/Samoa\n", + "UTC\n", + "Universal\n", + "W-SU\n", + "WET\n", + "Zulu\n" + ] + } + ], + "source": [ + "for tz in pytz.all_timezones:\n", + " print(tz)" + ] + }, + { + "cell_type": "markdown", + "id": "f18fa2c9", + "metadata": {}, + "source": [ + "With the `zoneinfo` module:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "9d2cf9d1", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Africa/Abidjan\n", + "Africa/Accra\n", + "Africa/Addis_Ababa\n", + "Africa/Algiers\n", + "Africa/Asmara\n", + "Africa/Asmera\n", + "Africa/Bamako\n", + "Africa/Bangui\n", + "Africa/Banjul\n", + "Africa/Bissau\n", + "Africa/Blantyre\n", + "Africa/Brazzaville\n", + "Africa/Bujumbura\n", + "Africa/Cairo\n", + "Africa/Casablanca\n", + "Africa/Ceuta\n", + "Africa/Conakry\n", + "Africa/Dakar\n", + "Africa/Dar_es_Salaam\n", + "Africa/Djibouti\n", + "Africa/Douala\n", + "Africa/El_Aaiun\n", + "Africa/Freetown\n", + "Africa/Gaborone\n", + "Africa/Harare\n", + "Africa/Johannesburg\n", + "Africa/Juba\n", + "Africa/Kampala\n", + "Africa/Khartoum\n", + "Africa/Kigali\n", + "Africa/Kinshasa\n", + "Africa/Lagos\n", + "Africa/Libreville\n", + "Africa/Lome\n", + "Africa/Luanda\n", + "Africa/Lubumbashi\n", + "Africa/Lusaka\n", + "Africa/Malabo\n", + "Africa/Maputo\n", + "Africa/Maseru\n", + "Africa/Mbabane\n", + "Africa/Mogadishu\n", + "Africa/Monrovia\n", + "Africa/Nairobi\n", + "Africa/Ndjamena\n", + "Africa/Niamey\n", + "Africa/Nouakchott\n", + "Africa/Ouagadougou\n", + "Africa/Porto-Novo\n", + "Africa/Sao_Tome\n", + "Africa/Timbuktu\n", + "Africa/Tripoli\n", + "Africa/Tunis\n", + "Africa/Windhoek\n", + "America/Adak\n", + "America/Anchorage\n", + "America/Anguilla\n", + "America/Antigua\n", + "America/Araguaina\n", + "America/Argentina/Buenos_Aires\n", + "America/Argentina/Catamarca\n", + "America/Argentina/ComodRivadavia\n", + "America/Argentina/Cordoba\n", + "America/Argentina/Jujuy\n", + "America/Argentina/La_Rioja\n", + "America/Argentina/Mendoza\n", + "America/Argentina/Rio_Gallegos\n", + "America/Argentina/Salta\n", + "America/Argentina/San_Juan\n", + "America/Argentina/San_Luis\n", + "America/Argentina/Tucuman\n", + "America/Argentina/Ushuaia\n", + "America/Aruba\n", + "America/Asuncion\n", + "America/Atikokan\n", + "America/Atka\n", + "America/Bahia\n", + "America/Bahia_Banderas\n", + "America/Barbados\n", + "America/Belem\n", + "America/Belize\n", + "America/Blanc-Sablon\n", + "America/Boa_Vista\n", + "America/Bogota\n", + "America/Boise\n", + "America/Buenos_Aires\n", + "America/Cambridge_Bay\n", + "America/Campo_Grande\n", + "America/Cancun\n", + "America/Caracas\n", + "America/Catamarca\n", + "America/Cayenne\n", + "America/Cayman\n", + "America/Chicago\n", + "America/Chihuahua\n", + "America/Coral_Harbour\n", + "America/Cordoba\n", + "America/Costa_Rica\n", + "America/Creston\n", + "America/Cuiaba\n", + "America/Curacao\n", + "America/Danmarkshavn\n", + "America/Dawson\n", + "America/Dawson_Creek\n", + "America/Denver\n", + "America/Detroit\n", + "America/Dominica\n", + "America/Edmonton\n", + "America/Eirunepe\n", + "America/El_Salvador\n", + "America/Ensenada\n", + "America/Fort_Nelson\n", + "America/Fort_Wayne\n", + "America/Fortaleza\n", + "America/Glace_Bay\n", + "America/Godthab\n", + "America/Goose_Bay\n", + "America/Grand_Turk\n", + "America/Grenada\n", + "America/Guadeloupe\n", + "America/Guatemala\n", + "America/Guayaquil\n", + "America/Guyana\n", + "America/Halifax\n", + "America/Havana\n", + "America/Hermosillo\n", + "America/Indiana/Indianapolis\n", + "America/Indiana/Knox\n", + "America/Indiana/Marengo\n", + "America/Indiana/Petersburg\n", + "America/Indiana/Tell_City\n", + "America/Indiana/Vevay\n", + "America/Indiana/Vincennes\n", + "America/Indiana/Winamac\n", + "America/Indianapolis\n", + "America/Inuvik\n", + "America/Iqaluit\n", + "America/Jamaica\n", + "America/Jujuy\n", + "America/Juneau\n", + "America/Kentucky/Louisville\n", + "America/Kentucky/Monticello\n", + "America/Knox_IN\n", + "America/Kralendijk\n", + "America/La_Paz\n", + "America/Lima\n", + "America/Los_Angeles\n", + "America/Louisville\n", + "America/Lower_Princes\n", + "America/Maceio\n", + "America/Managua\n", + "America/Manaus\n", + "America/Marigot\n", + "America/Martinique\n", + "America/Matamoros\n", + "America/Mazatlan\n", + "America/Mendoza\n", + "America/Menominee\n", + "America/Merida\n", + "America/Metlakatla\n", + "America/Mexico_City\n", + "America/Miquelon\n", + "America/Moncton\n", + "America/Monterrey\n", + "America/Montevideo\n", + "America/Montreal\n", + "America/Montserrat\n", + "America/Nassau\n", + "America/New_York\n", + "America/Nipigon\n", + "America/Nome\n", + "America/Noronha\n", + "America/North_Dakota/Beulah\n", + "America/North_Dakota/Center\n", + "America/North_Dakota/New_Salem\n", + "America/Nuuk\n", + "America/Ojinaga\n", + "America/Panama\n", + "America/Pangnirtung\n", + "America/Paramaribo\n", + "America/Phoenix\n", + "America/Port-au-Prince\n", + "America/Port_of_Spain\n", + "America/Porto_Acre\n", + "America/Porto_Velho\n", + "America/Puerto_Rico\n", + "America/Punta_Arenas\n", + "America/Rainy_River\n", + "America/Rankin_Inlet\n", + "America/Recife\n", + "America/Regina\n", + "America/Resolute\n", + "America/Rio_Branco\n", + "America/Rosario\n", + "America/Santa_Isabel\n", + "America/Santarem\n", + "America/Santiago\n", + "America/Santo_Domingo\n", + "America/Sao_Paulo\n", + "America/Scoresbysund\n", + "America/Shiprock\n", + "America/Sitka\n", + "America/St_Barthelemy\n", + "America/St_Johns\n", + "America/St_Kitts\n", + "America/St_Lucia\n", + "America/St_Thomas\n", + "America/St_Vincent\n", + "America/Swift_Current\n", + "America/Tegucigalpa\n", + "America/Thule\n", + "America/Thunder_Bay\n", + "America/Tijuana\n", + "America/Toronto\n", + "America/Tortola\n", + "America/Vancouver\n", + "America/Virgin\n", + "America/Whitehorse\n", + "America/Winnipeg\n", + "America/Yakutat\n", + "America/Yellowknife\n", + "Antarctica/Casey\n", + "Antarctica/Davis\n", + "Antarctica/DumontDUrville\n", + "Antarctica/Macquarie\n", + "Antarctica/Mawson\n", + "Antarctica/McMurdo\n", + "Antarctica/Palmer\n", + "Antarctica/Rothera\n", + "Antarctica/South_Pole\n", + "Antarctica/Syowa\n", + "Antarctica/Troll\n", + "Antarctica/Vostok\n", + "Arctic/Longyearbyen\n", + "Asia/Aden\n", + "Asia/Almaty\n", + "Asia/Amman\n", + "Asia/Anadyr\n", + "Asia/Aqtau\n", + "Asia/Aqtobe\n", + "Asia/Ashgabat\n", + "Asia/Ashkhabad\n", + "Asia/Atyrau\n", + "Asia/Baghdad\n", + "Asia/Bahrain\n", + "Asia/Baku\n", + "Asia/Bangkok\n", + "Asia/Barnaul\n", + "Asia/Beirut\n", + "Asia/Bishkek\n", + "Asia/Brunei\n", + "Asia/Calcutta\n", + "Asia/Chita\n", + "Asia/Choibalsan\n", + "Asia/Chongqing\n", + "Asia/Chungking\n", + "Asia/Colombo\n", + "Asia/Dacca\n", + "Asia/Damascus\n", + "Asia/Dhaka\n", + "Asia/Dili\n", + "Asia/Dubai\n", + "Asia/Dushanbe\n", + "Asia/Famagusta\n", + "Asia/Gaza\n", + "Asia/Harbin\n", + "Asia/Hebron\n", + "Asia/Ho_Chi_Minh\n", + "Asia/Hong_Kong\n", + "Asia/Hovd\n", + "Asia/Irkutsk\n", + "Asia/Istanbul\n", + "Asia/Jakarta\n", + "Asia/Jayapura\n", + "Asia/Jerusalem\n", + "Asia/Kabul\n", + "Asia/Kamchatka\n", + "Asia/Karachi\n", + "Asia/Kashgar\n", + "Asia/Kathmandu\n", + "Asia/Katmandu\n", + "Asia/Khandyga\n", + "Asia/Kolkata\n", + "Asia/Krasnoyarsk\n", + "Asia/Kuala_Lumpur\n", + "Asia/Kuching\n", + "Asia/Kuwait\n", + "Asia/Macao\n", + "Asia/Macau\n", + "Asia/Magadan\n", + "Asia/Makassar\n", + "Asia/Manila\n", + "Asia/Muscat\n", + "Asia/Nicosia\n", + "Asia/Novokuznetsk\n", + "Asia/Novosibirsk\n", + "Asia/Omsk\n", + "Asia/Oral\n", + "Asia/Phnom_Penh\n", + "Asia/Pontianak\n", + "Asia/Pyongyang\n", + "Asia/Qatar\n", + "Asia/Qostanay\n", + "Asia/Qyzylorda\n", + "Asia/Rangoon\n", + "Asia/Riyadh\n", + "Asia/Saigon\n", + "Asia/Sakhalin\n", + "Asia/Samarkand\n", + "Asia/Seoul\n", + "Asia/Shanghai\n", + "Asia/Singapore\n", + "Asia/Srednekolymsk\n", + "Asia/Taipei\n", + "Asia/Tashkent\n", + "Asia/Tbilisi\n", + "Asia/Tehran\n", + "Asia/Tel_Aviv\n", + "Asia/Thimbu\n", + "Asia/Thimphu\n", + "Asia/Tokyo\n", + "Asia/Tomsk\n", + "Asia/Ujung_Pandang\n", + "Asia/Ulaanbaatar\n", + "Asia/Ulan_Bator\n", + "Asia/Urumqi\n", + "Asia/Ust-Nera\n", + "Asia/Vientiane\n", + "Asia/Vladivostok\n", + "Asia/Yakutsk\n", + "Asia/Yangon\n", + "Asia/Yekaterinburg\n", + "Asia/Yerevan\n", + "Atlantic/Azores\n", + "Atlantic/Bermuda\n", + "Atlantic/Canary\n", + "Atlantic/Cape_Verde\n", + "Atlantic/Faeroe\n", + "Atlantic/Faroe\n", + "Atlantic/Jan_Mayen\n", + "Atlantic/Madeira\n", + "Atlantic/Reykjavik\n", + "Atlantic/South_Georgia\n", + "Atlantic/St_Helena\n", + "Atlantic/Stanley\n", + "Australia/ACT\n", + "Australia/Adelaide\n", + "Australia/Brisbane\n", + "Australia/Broken_Hill\n", + "Australia/Canberra\n", + "Australia/Currie\n", + "Australia/Darwin\n", + "Australia/Eucla\n", + "Australia/Hobart\n", + "Australia/LHI\n", + "Australia/Lindeman\n", + "Australia/Lord_Howe\n", + "Australia/Melbourne\n", + "Australia/NSW\n", + "Australia/North\n", + "Australia/Perth\n", + "Australia/Queensland\n", + "Australia/South\n", + "Australia/Sydney\n", + "Australia/Tasmania\n", + "Australia/Victoria\n", + "Australia/West\n", + "Australia/Yancowinna\n", + "Brazil/Acre\n", + "Brazil/DeNoronha\n", + "Brazil/East\n", + "Brazil/West\n", + "CET\n", + "CST6CDT\n", + "Canada/Atlantic\n", + "Canada/Central\n", + "Canada/Eastern\n", + "Canada/Mountain\n", + "Canada/Newfoundland\n", + "Canada/Pacific\n", + "Canada/Saskatchewan\n", + "Canada/Yukon\n", + "Chile/Continental\n", + "Chile/EasterIsland\n", + "Cuba\n", + "EET\n", + "EST\n", + "EST5EDT\n", + "Egypt\n", + "Eire\n", + "Etc/GMT\n", + "Etc/GMT+0\n", + "Etc/GMT+1\n", + "Etc/GMT+10\n", + "Etc/GMT+11\n", + "Etc/GMT+12\n", + "Etc/GMT+2\n", + "Etc/GMT+3\n", + "Etc/GMT+4\n", + "Etc/GMT+5\n", + "Etc/GMT+6\n", + "Etc/GMT+7\n", + "Etc/GMT+8\n", + "Etc/GMT+9\n", + "Etc/GMT-0\n", + "Etc/GMT-1\n", + "Etc/GMT-10\n", + "Etc/GMT-11\n", + "Etc/GMT-12\n", + "Etc/GMT-13\n", + "Etc/GMT-14\n", + "Etc/GMT-2\n", + "Etc/GMT-3\n", + "Etc/GMT-4\n", + "Etc/GMT-5\n", + "Etc/GMT-6\n", + "Etc/GMT-7\n", + "Etc/GMT-8\n", + "Etc/GMT-9\n", + "Etc/GMT0\n", + "Etc/Greenwich\n", + "Etc/UCT\n", + "Etc/UTC\n", + "Etc/Universal\n", + "Etc/Zulu\n", + "Europe/Amsterdam\n", + "Europe/Andorra\n", + "Europe/Astrakhan\n", + "Europe/Athens\n", + "Europe/Belfast\n", + "Europe/Belgrade\n", + "Europe/Berlin\n", + "Europe/Bratislava\n", + "Europe/Brussels\n", + "Europe/Bucharest\n", + "Europe/Budapest\n", + "Europe/Busingen\n", + "Europe/Chisinau\n", + "Europe/Copenhagen\n", + "Europe/Dublin\n", + "Europe/Gibraltar\n", + "Europe/Guernsey\n", + "Europe/Helsinki\n", + "Europe/Isle_of_Man\n", + "Europe/Istanbul\n", + "Europe/Jersey\n", + "Europe/Kaliningrad\n", + "Europe/Kiev\n", + "Europe/Kirov\n", + "Europe/Lisbon\n", + "Europe/Ljubljana\n", + "Europe/London\n", + "Europe/Luxembourg\n", + "Europe/Madrid\n", + "Europe/Malta\n", + "Europe/Mariehamn\n", + "Europe/Minsk\n", + "Europe/Monaco\n", + "Europe/Moscow\n", + "Europe/Nicosia\n", + "Europe/Oslo\n", + "Europe/Paris\n", + "Europe/Podgorica\n", + "Europe/Prague\n", + "Europe/Riga\n", + "Europe/Rome\n", + "Europe/Samara\n", + "Europe/San_Marino\n", + "Europe/Sarajevo\n", + "Europe/Saratov\n", + "Europe/Simferopol\n", + "Europe/Skopje\n", + "Europe/Sofia\n", + "Europe/Stockholm\n", + "Europe/Tallinn\n", + "Europe/Tirane\n", + "Europe/Tiraspol\n", + "Europe/Ulyanovsk\n", + "Europe/Uzhgorod\n", + "Europe/Vaduz\n", + "Europe/Vatican\n", + "Europe/Vienna\n", + "Europe/Vilnius\n", + "Europe/Volgograd\n", + "Europe/Warsaw\n", + "Europe/Zagreb\n", + "Europe/Zaporozhye\n", + "Europe/Zurich\n", + "Factory\n", + "GB\n", + "GB-Eire\n", + "GMT\n", + "GMT+0\n", + "GMT-0\n", + "GMT0\n", + "Greenwich\n", + "HST\n", + "Hongkong\n", + "Iceland\n", + "Indian/Antananarivo\n", + "Indian/Chagos\n", + "Indian/Christmas\n", + "Indian/Cocos\n", + "Indian/Comoro\n", + "Indian/Kerguelen\n", + "Indian/Mahe\n", + "Indian/Maldives\n", + "Indian/Mauritius\n", + "Indian/Mayotte\n", + "Indian/Reunion\n", + "Iran\n", + "Israel\n", + "Jamaica\n", + "Japan\n", + "Kwajalein\n", + "Libya\n", + "MET\n", + "MST\n", + "MST7MDT\n", + "Mexico/BajaNorte\n", + "Mexico/BajaSur\n", + "Mexico/General\n", + "NZ\n", + "NZ-CHAT\n", + "Navajo\n", + "PRC\n", + "PST8PDT\n", + "Pacific/Apia\n", + "Pacific/Auckland\n", + "Pacific/Bougainville\n", + "Pacific/Chatham\n", + "Pacific/Chuuk\n", + "Pacific/Easter\n", + "Pacific/Efate\n", + "Pacific/Enderbury\n", + "Pacific/Fakaofo\n", + "Pacific/Fiji\n", + "Pacific/Funafuti\n", + "Pacific/Galapagos\n", + "Pacific/Gambier\n", + "Pacific/Guadalcanal\n", + "Pacific/Guam\n", + "Pacific/Honolulu\n", + "Pacific/Johnston\n", + "Pacific/Kanton\n", + "Pacific/Kiritimati\n", + "Pacific/Kosrae\n", + "Pacific/Kwajalein\n", + "Pacific/Majuro\n", + "Pacific/Marquesas\n", + "Pacific/Midway\n", + "Pacific/Nauru\n", + "Pacific/Niue\n", + "Pacific/Norfolk\n", + "Pacific/Noumea\n", + "Pacific/Pago_Pago\n", + "Pacific/Palau\n", + "Pacific/Pitcairn\n", + "Pacific/Pohnpei\n", + "Pacific/Ponape\n", + "Pacific/Port_Moresby\n", + "Pacific/Rarotonga\n", + "Pacific/Saipan\n", + "Pacific/Samoa\n", + "Pacific/Tahiti\n", + "Pacific/Tarawa\n", + "Pacific/Tongatapu\n", + "Pacific/Truk\n", + "Pacific/Wake\n", + "Pacific/Wallis\n", + "Pacific/Yap\n", + "Poland\n", + "Portugal\n", + "ROC\n", + "ROK\n", + "Singapore\n", + "Turkey\n", + "UCT\n", + "US/Alaska\n", + "US/Aleutian\n", + "US/Arizona\n", + "US/Central\n", + "US/East-Indiana\n", + "US/Eastern\n", + "US/Hawaii\n", + "US/Indiana-Starke\n", + "US/Michigan\n", + "US/Mountain\n", + "US/Pacific\n", + "US/Samoa\n", + "UTC\n", + "Universal\n", + "W-SU\n", + "WET\n", + "Zulu\n" + ] + } + ], + "source": [ + "for tz in sorted(zoneinfo.available_timezones()):\n", + " print(tz)" + ] + }, + { + "cell_type": "markdown", + "id": "6a1887cf", + "metadata": {}, + "source": [ + "Are the time zones defined by `pytz` and `zoneinfo` the same? Yes!" + ] + }, + { + "cell_type": "markdown", + "id": "8f8df276", + "metadata": {}, + "source": [ + "In this example, let's take our current time in UTC, and convert it to some other time zone, say `Australia/Melbourne`." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "77f1b8d9", + "metadata": {}, + "outputs": [], + "source": [ + "now_utc_naive = datetime.utcnow()" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "f8586215", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "datetime.datetime(2022, 3, 20, 6, 1, 3, 368403)" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "now_utc_naive" + ] + }, + { + "cell_type": "markdown", + "id": "a452220a", + "metadata": {}, + "source": [ + "The problem here is that we have a _naive_ datetime (i.e. one without an attached timezone)." + ] + }, + { + "cell_type": "markdown", + "id": "d1d10809", + "metadata": {}, + "source": [ + "We can make this naive datetime time zone aware by tacking on the timezone (since we know it is UTC):" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "db60736b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "datetime.datetime(2022, 3, 20, 6, 1, 3, 368403, tzinfo=datetime.timezone.utc)" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "now_utc_aware = now_utc_naive.replace(tzinfo=timezone.utc)\n", + "now_utc_aware" + ] + }, + { + "cell_type": "markdown", + "id": "8282b097", + "metadata": {}, + "source": [ + "Or, we could use the `pytz` library to do the same thing:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "0d7434f7", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "datetime.datetime(2022, 3, 20, 6, 1, 3, 377626, tzinfo=)" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "pytz.utc.localize(datetime.utcnow())" + ] + }, + { + "cell_type": "markdown", + "id": "e613c2e1", + "metadata": {}, + "source": [ + "Now that we have a time zone aware datetime, we can convert it to another timezone using `pytz`:" + ] + }, + { + "cell_type": "markdown", + "id": "94b0b5c9", + "metadata": {}, + "source": [ + "First, let's pick a time zone from `pytz`:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "833b5ff9", + "metadata": {}, + "outputs": [], + "source": [ + "tz_melbourne = pytz.timezone('Australia/Melbourne')" + ] + }, + { + "cell_type": "markdown", + "id": "ce4323c1", + "metadata": {}, + "source": [ + "And now we localize our aware datetime to this time zone:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "d23fd3c4", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "datetime.datetime(2022, 3, 20, 17, 1, 3, 368403, tzinfo=)" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "now_utc_aware.astimezone(tz_melbourne)" + ] + }, + { + "cell_type": "markdown", + "id": "260bd7ff", + "metadata": {}, + "source": [ + "We could do both these steps in a single expression:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "6559daf7", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "datetime.datetime(2022, 3, 20, 17, 1, 3, 368403, tzinfo=)" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "now_utc_aware.astimezone(pytz.timezone('Australia/Melbourne'))" + ] + }, + { + "cell_type": "markdown", + "id": "6be99718", + "metadata": {}, + "source": [ + "Now, let's do the same thing using the `zoneinfo` module." + ] + }, + { + "cell_type": "markdown", + "id": "8c9b22bb", + "metadata": {}, + "source": [ + "Let's pick the same target time zone:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "30c58db3", + "metadata": {}, + "outputs": [], + "source": [ + "tz_zi_dublin = ZoneInfo(\"Europe/Dublin\")" + ] + }, + { + "cell_type": "markdown", + "id": "0f77b5b0", + "metadata": {}, + "source": [ + "And the let's convert our aware datetime to that time zone:" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "1623322d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "datetime.datetime(2022, 3, 20, 6, 1, 3, 368403, tzinfo=zoneinfo.ZoneInfo(key='Europe/Dublin'))" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "now_utc_aware.astimezone(tz_zi_dublin)" + ] + }, + { + "cell_type": "markdown", + "id": "4536470f", + "metadata": {}, + "source": [ + "Or, we can also write this as a single expression:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "337c3955", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "datetime.datetime(2022, 3, 20, 6, 1, 3, 368403, tzinfo=zoneinfo.ZoneInfo(key='Europe/Dublin'))" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "now_utc_aware.astimezone(ZoneInfo(\"Europe/Dublin\"))" + ] + }, + { + "cell_type": "markdown", + "id": "4076195d", + "metadata": {}, + "source": [ + "#### The `math` Module" + ] + }, + { + "cell_type": "markdown", + "id": "41d86e28", + "metadata": {}, + "source": [ + "Several enhancements or additions have been to the math library." + ] + }, + { + "cell_type": "markdown", + "id": "bd3de95f", + "metadata": {}, + "source": [ + "The `math` module already had the `gcd` function to calculate the great common divisor of two numbers:" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "911df27e", + "metadata": {}, + "outputs": [], + "source": [ + "import math" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "4154629e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "9" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "math.gcd(27, 45)" + ] + }, + { + "cell_type": "markdown", + "id": "85abe1fa", + "metadata": {}, + "source": [ + "But now `gcd` can take multiple arguments, not just two:" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "d64ea0d9", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "math.gcd(27, 45, 18, 15)" + ] + }, + { + "cell_type": "markdown", + "id": "0503d78a", + "metadata": {}, + "source": [ + "The `lcm` (least common multiple) function has been added:" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "b4c25fef", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "12" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "math.lcm(2, 3, 4)" + ] + }, + { + "cell_type": "markdown", + "id": "58a4c3cd", + "metadata": {}, + "source": [ + "#### Dictionary Unions" + ] + }, + { + "cell_type": "markdown", + "id": "2c05f130", + "metadata": {}, + "source": [ + "When we discussed dictionaries in this course, we saw that we could combine two dictionaries using unpacking:" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "136c8836", + "metadata": {}, + "outputs": [], + "source": [ + "d1 = {'a': 1, 'b': 2, 'c': 3}\n", + "d2 = {'c': 30, 'd': 40}" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "81331802", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'a': 1, 'b': 2, 'c': 30, 'd': 40}" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "{**d1, **d2}" + ] + }, + { + "cell_type": "markdown", + "id": "97ca030f", + "metadata": {}, + "source": [ + "As we saw the second dictionary's key/value pair \"overwrote\" the key/value pair from the first dictionary." + ] + }, + { + "cell_type": "markdown", + "id": "b361771e", + "metadata": {}, + "source": [ + "We could also use the `ChainMap` function in the `collections` module:" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "d633f16c", + "metadata": {}, + "outputs": [], + "source": [ + "from collections import ChainMap" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "ad250dfd", + "metadata": {}, + "outputs": [], + "source": [ + "merged = ChainMap(d1, d2)" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "1deeb8f2", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(1, 3, 40)" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "merged['a'], merged['c'], merged['d']" + ] + }, + { + "cell_type": "markdown", + "id": "1751d47a", + "metadata": {}, + "source": [ + "As you can see, in the `ChainMap`, the firest occurrence of the key is used - so in this case `c` comes from `d1`, not `d2`." + ] + }, + { + "cell_type": "markdown", + "id": "1c63e893", + "metadata": {}, + "source": [ + "Both of these ways of \"combining\" dictionaries work well - but they are not very intuitive, and need a little attention to what happens when you have common keys in the dictionaries." + ] + }, + { + "cell_type": "markdown", + "id": "aca5787b", + "metadata": {}, + "source": [ + "Think of concatenating lists where we can simply use the `+` operator - this is very intuitive:" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "de850747", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[1, 2, 3, 4, 5, 6]" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "[1, 2, 3] + [4, 5, 6]" + ] + }, + { + "cell_type": "markdown", + "id": "69429bab", + "metadata": {}, + "source": [ + "Now dictionaries are not like lists, but they are closely related to **sets**. With sets, we have the **union** operator (`|`):" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "dcb8a7b5", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'a', 'b', 'c', 'd'}" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "s1 = {'a', 'b', 'c'}\n", + "s2 = {'c', 'd'}\n", + "\n", + "s1 | s2" + ] + }, + { + "cell_type": "markdown", + "id": "32c20dfb", + "metadata": {}, + "source": [ + "Python 3.9 introduces support for the **union** (`|`) operation between dictionaries as well." + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "af3fcc01", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'a': 1, 'b': 2, 'c': 30, 'd': 40}" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "d1 | d2" + ] + }, + { + "cell_type": "markdown", + "id": "851ffadc", + "metadata": {}, + "source": [ + "Just like with the `{**d1, **d2}` approach, the value for `c` came from the second dictionary." + ] + }, + { + "cell_type": "markdown", + "id": "cb6fc060", + "metadata": {}, + "source": [ + "And just like with that technique we can control this by switching the order of the dictionaries in the union:" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "05555100", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'c': 3, 'd': 40, 'a': 1, 'b': 2}" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "d2 | d1" + ] + }, + { + "cell_type": "markdown", + "id": "739e210f", + "metadata": {}, + "source": [ + "One question we should have, is what happens to the insertion order that Python dictionaries now guarantee?" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "f8759da5", + "metadata": {}, + "outputs": [], + "source": [ + "d1 = {'c': 3, 'a': 1, 'b': 2}\n", + "d2 = {'d': 40, 'c': 30}" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "e06e3966", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'c': 30, 'a': 1, 'b': 2, 'd': 40}" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "d1 | d2" + ] + }, + { + "cell_type": "markdown", + "id": "8e0e1c2e", + "metadata": {}, + "source": [ + "As you can see, even though the **value** for `c` came from the **second** dictionary, the original inertion order of the **keys** is maintained, so `c` is still in first position in the union of the two dictionaries." + ] + }, + { + "cell_type": "markdown", + "id": "e12dee6a", + "metadata": {}, + "source": [ + "#### String Methods" + ] + }, + { + "cell_type": "markdown", + "id": "f69f79e4", + "metadata": {}, + "source": [ + "Often we need to remove some prefix or suffix in a string." + ] + }, + { + "cell_type": "markdown", + "id": "67496793", + "metadata": {}, + "source": [ + "For example, we may have this list of string:" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "9da4cabe", + "metadata": {}, + "outputs": [], + "source": [ + "data = [\n", + " \"(log) [2022-03-01T13:30:01] Log record 1\",\n", + " \"(log) [2022-03-01T13:30:02] Log record 2\",\n", + " \"(log) [2022-03-01T13:30:03] Log record 3\",\n", + " \"(log) [2022-03-01T13:30:04] Log record 4\",\n", + "]" + ] + }, + { + "cell_type": "markdown", + "id": "30de1de8", + "metadata": {}, + "source": [ + "And we want to clean these up and remove the `(log) ` prefix (including the space)." + ] + }, + { + "cell_type": "markdown", + "id": "84a6c502", + "metadata": {}, + "source": [ + "We can certainly do it this way:" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "8a60ecdc", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['[2022-03-01T13:30:01] Log record 1',\n", + " '[2022-03-01T13:30:02] Log record 2',\n", + " '[2022-03-01T13:30:03] Log record 3',\n", + " '[2022-03-01T13:30:04] Log record 4']" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "clean = [\n", + " s.replace(\"(log) \", '')\n", + " for s in data\n", + "]\n", + "clean" + ] + }, + { + "cell_type": "markdown", + "id": "45fa082e", + "metadata": {}, + "source": [ + "You might be tempted to use the `lstrip` method:" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "id": "74e60a43", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['[2022-03-01T13:30:01] Log record 1',\n", + " '[2022-03-01T13:30:02] Log record 2',\n", + " '[2022-03-01T13:30:03] Log record 3',\n", + " '[2022-03-01T13:30:04] Log record 4']" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "clean = [\n", + " s.lstrip(\"(log) \")\n", + " for s in data\n", + "]\n", + "clean" + ] + }, + { + "cell_type": "markdown", + "id": "5e045ebc", + "metadata": {}, + "source": [ + "This appears to work, but `lstrip` (and `rstrip`) does not interpet `\"(log )\"` as a string, but rather a **sequence** of characters, and each one will be removed - so you might end up with this problem:" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "id": "ddc6e3a8", + "metadata": {}, + "outputs": [], + "source": [ + "data2 = [\n", + " \"(log) log: [2022-03-01T13:30:01] Log record 1\",\n", + " \"(log) log: [2022-03-01T13:30:02] Log record 2\",\n", + " \"(log) log: [2022-03-01T13:30:03] Log record 3\",\n", + " \"(log) log: [2022-03-01T13:30:04] Log record 4\",\n", + "]" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "id": "d87cd3c5", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[': [2022-03-01T13:30:01] Log record 1',\n", + " ': [2022-03-01T13:30:02] Log record 2',\n", + " ': [2022-03-01T13:30:03] Log record 3',\n", + " ': [2022-03-01T13:30:04] Log record 4']" + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "clean = [\n", + " s.lstrip(\"(log) \")\n", + " for s in data2\n", + "]\n", + "clean" + ] + }, + { + "cell_type": "markdown", + "id": "0f73aac3", + "metadata": {}, + "source": [ + "Now that removed a lot more than expected everything from those strings, unlike the replace, which will replace only the first occurrence by default:" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "id": "eaf8792b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['log: [2022-03-01T13:30:01] Log record 1',\n", + " 'log: [2022-03-01T13:30:02] Log record 2',\n", + " 'log: [2022-03-01T13:30:03] Log record 3',\n", + " 'log: [2022-03-01T13:30:04] Log record 4']" + ] + }, + "execution_count": 34, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "clean = [\n", + " s.replace(\"(log) \", '')\n", + " for s in data2\n", + "]\n", + "clean" + ] + }, + { + "cell_type": "markdown", + "id": "0aca0211", + "metadata": {}, + "source": [ + "Python 3,9 introduces two new string methods to do this without having to use `replace`, namely the `removeprefix()` and `removesuffix()` methods:" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "id": "b320eb46", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['[2022-03-01T13:30:01] Log record 1',\n", + " '[2022-03-01T13:30:02] Log record 2',\n", + " '[2022-03-01T13:30:03] Log record 3',\n", + " '[2022-03-01T13:30:04] Log record 4']" + ] + }, + "execution_count": 35, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "[\n", + " s.removeprefix(\"(log) \")\n", + " for s in data\n", + "]" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "id": "d4ebaf46", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['log: [2022-03-01T13:30:01] Log record 1',\n", + " 'log: [2022-03-01T13:30:02] Log record 2',\n", + " 'log: [2022-03-01T13:30:03] Log record 3',\n", + " 'log: [2022-03-01T13:30:04] Log record 4']" + ] + }, + "execution_count": 36, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "[\n", + " s.removeprefix(\"(log) \")\n", + " for s in data2\n", + "]" + ] + }, + { + "cell_type": "markdown", + "id": "f7a2ffd4", + "metadata": {}, + "source": [ + "Note that if the prefix (or suffix) is not found, nothing happens, the new string will be the same as the original (i.e. no exception is raised):" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "id": "829cb63b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'Python rocks!'" + ] + }, + "execution_count": 37, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "'Python rocks!'.removeprefix('Java')" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.10.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Part 2/Section 14 - Extras/03 - Relevant Python 3.10 Changes.ipynb b/Part 2/Section 14 - Extras/03 - Relevant Python 3.10 Changes.ipynb new file mode 100644 index 00000000..1e7fa9b4 --- /dev/null +++ b/Part 2/Section 14 - Extras/03 - Relevant Python 3.10 Changes.ipynb @@ -0,0 +1,991 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "c4d4fc49", + "metadata": {}, + "source": [ + "### Relevant Python 3.10 Changes" + ] + }, + { + "cell_type": "markdown", + "id": "cb1495a9", + "metadata": {}, + "source": [ + "The release of Python 3.10 has brought some new features.\n", + "\n", + "This is a summary of the ones _I_ deemed relevant to this course, and does **not** include all the changes!\n", + "\n", + "For full release details, see [here](https://docs.python.org/3/whatsnew/3.10.html)" + ] + }, + { + "cell_type": "markdown", + "id": "98c3ae0d", + "metadata": {}, + "source": [ + "Python 3.10 has improved it's error messages for syntax errors - the messages are more helpful and hopefully give you a better understanding of what may be syntactically wrong with your code." + ] + }, + { + "cell_type": "markdown", + "id": "4ae4aa37", + "metadata": {}, + "source": [ + "#### Structural Pattern Matching" + ] + }, + { + "cell_type": "markdown", + "id": "d18177a2", + "metadata": {}, + "source": [ + "One thing I often hear people ask, is, what's the Python equivalent of a `switch` statement." + ] + }, + { + "cell_type": "markdown", + "id": "36a77514", + "metadata": {}, + "source": [ + "Until now, the answer has alwasy been - there isn't one. Use `if...elif` constructs." + ] + }, + { + "cell_type": "markdown", + "id": "56fdb138", + "metadata": {}, + "source": [ + "Python 3.10 introduces a new language element (`match`) to implement something called **pattern matching**, that can be used to replicate this `switch` behavior you might be used to in other languages." + ] + }, + { + "cell_type": "markdown", + "id": "e1cf8b9d", + "metadata": {}, + "source": [ + "I'll cover some of the basics here, but you should refer to the Python [docs](https://docs.python.org/3/reference/compound_stmts.html#the-match-statement) for more information, as well as the [pep](https://peps.python.org/pep-0634/) for this feature and a [tutorial pep](https://peps.python.org/pep-0636/)." + ] + }, + { + "cell_type": "markdown", + "id": "1417630a", + "metadata": {}, + "source": [ + "Let's start with a simple `match` statement:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "bbecc554", + "metadata": {}, + "outputs": [], + "source": [ + "def respond(language):\n", + " match language:\n", + " case \"Java\":\n", + " return \"Hmm, coffee!\"\n", + " case \"Python\":\n", + " return \"I'm not scared of snakes!\"\n", + " case \"Rust\":\n", + " return \"Don't drink too much water!\"\n", + " case \"Go\":\n", + " return \"Collect $200\"\n", + " case _:\n", + " return \"I'm sorry...\"" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "d5669e92", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"I'm not scared of snakes!\"" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "respond(\"Python\")" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "90cecc86", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'Collect $200'" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "respond(\"Go\")" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "f9feb023", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"I'm sorry...\"" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "respond(\"COBOL\")" + ] + }, + { + "cell_type": "markdown", + "id": "ee7c21fb", + "metadata": {}, + "source": [ + "Here we were able to define a \"default\" match pattern by using the underscore (`_`) as our pattern - this `_` is called a **wildcard**." + ] + }, + { + "cell_type": "markdown", + "id": "b76575de", + "metadata": {}, + "source": [ + "So this is very much like the \"plain\" switch statement found in some other languages." + ] + }, + { + "cell_type": "markdown", + "id": "6e55dd9f", + "metadata": {}, + "source": [ + "But, this is where things get ineteresting, pattern matching can do much more than the simple example we just saw." + ] + }, + { + "cell_type": "markdown", + "id": "f8d495f2", + "metadata": {}, + "source": [ + "For example, you can have multiple pattern matching:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "6d5fcc0c", + "metadata": {}, + "outputs": [], + "source": [ + "def respond(language):\n", + " match language:\n", + " case \"Java\" | \"Javascript\":\n", + " return \"Love those braces!\"\n", + " case \"Python\":\n", + " return \"I'm a lumberjack and I don't need no braces\"\n", + " case _:\n", + " return \"I have no clue!\"" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "02cf21af", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'Love those braces!'" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "respond(\"Java\")" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "853368f3", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'Love those braces!'" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "respond(\"Javascript\")" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "066f985e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"I'm a lumberjack and I don't need no braces\"" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "respond(\"Python\")" + ] + }, + { + "cell_type": "markdown", + "id": "7fc43e7f", + "metadata": {}, + "source": [ + "We could match against one or more literals by using the OR pattern (`|`)" + ] + }, + { + "cell_type": "markdown", + "id": "36064a9c", + "metadata": {}, + "source": [ + "Let's look at one more example, this time matching **multiple values**." + ] + }, + { + "cell_type": "markdown", + "id": "31978910", + "metadata": {}, + "source": [ + "Suppose we have some kind of command language for driving a remote controlled robot in a maze, picking up and dropping items as it moves around. Our robot is very simple, it can move in only a few directions, and one step at a time. So to move forward three spaces, we would issue three `move forward` commands.\n", + "\n", + "Additional commands are `move backward`, `move left`, `move right`. We also have a few other commands our robot understands: `pick` and `drop` for picking up and dropping objects it might find." + ] + }, + { + "cell_type": "markdown", + "id": "63944ea5", + "metadata": {}, + "source": [ + "We might write a command interpreter this way:" + ] + }, + { + "cell_type": "markdown", + "id": "f9077f13", + "metadata": {}, + "source": [ + "Let's start by using some symbols to represent the robot's actions:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "2dde5d86", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'F': '→', 'B': '←', 'L': '↑', 'R': '↓', 'pick': '⤣', 'drop': '⤥'}" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "symbols = {\n", + " \"F\": \"\\u2192\", \n", + " \"B\": \"\\u2190\", \n", + " \"L\": \"\\u2191\", \n", + " \"R\": \"\\u2193\", \n", + " \"pick\": \"\\u2923\", \n", + " \"drop\": \"\\u2925\"\n", + "}\n", + "\n", + "symbols" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "faab4c09", + "metadata": {}, + "outputs": [], + "source": [ + "def op(command):\n", + " match command:\n", + " case \"move F\":\n", + " return symbols[\"F\"]\n", + " case \"move B\":\n", + " return symbols[\"B\"]\n", + " case \"move L\":\n", + " return symbols[\"L\"]\n", + " case \"move R\":\n", + " return symbols[\"R\"]\n", + " case \"pick\":\n", + " return symbols[\"pick\"]\n", + " case \"drop\":\n", + " return symbols[\"drop\"]\n", + " case _:\n", + " raise ValueError(f\"{command} does not compute!\")" + ] + }, + { + "cell_type": "markdown", + "id": "24555fab", + "metadata": {}, + "source": [ + "Then we could issue commands such as:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "b673a044", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'↑'" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "op(\"move L\")" + ] + }, + { + "cell_type": "markdown", + "id": "b411438f", + "metadata": {}, + "source": [ + "Or multiple sequences by maybe using a list of such commands, effectively creating a sequential program for our robot:" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "cf94014f", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['→', '→', '↑', '⤣', '↓', '↑', '→', '⤥']" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "[\n", + " op(\"move F\"),\n", + " op(\"move F\"),\n", + " op(\"move L\"),\n", + " op(\"pick\"),\n", + " op(\"move R\"),\n", + " op(\"move L\"),\n", + " op(\"move F\"),\n", + " op(\"drop\"),\n", + "]" + ] + }, + { + "cell_type": "markdown", + "id": "c520053b", + "metadata": {}, + "source": [ + "We could use something called **capturing** matched sub-patterns to simply our code somewhat:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "e7da20cf", + "metadata": {}, + "outputs": [], + "source": [ + "def op(command):\n", + " match command:\n", + " case [\"move\", (\"F\" | \"B\" | \"L\" |\"R\") as direction]:\n", + " return symbols[direction]\n", + " case \"pick\":\n", + " return symbols[\"pick\"]\n", + " case \"drop\":\n", + " return symvols[\"drop\"]\n", + " case _:\n", + " raise ValueError(f\"{command} does not compute!\")" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "c2a1c617", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'↑'" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "op([\"move\", \"L\"])" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "4edeb3dc", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'⤣'" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "op(\"pick\")" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "b13504aa", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "fly does not compute!\n" + ] + } + ], + "source": [ + "try:\n", + " op(\"fly\")\n", + "except ValueError as ex:\n", + " print(ex)" + ] + }, + { + "cell_type": "markdown", + "id": "6769c228", + "metadata": {}, + "source": [ + "This is kind of tedious, it would be nicer to write commands such as `move F F L` and `move R L F` instead." + ] + }, + { + "cell_type": "markdown", + "id": "055d2c60", + "metadata": {}, + "source": [ + "There are many ways we could solve this, but pattern matching on multiple values can be really useful here." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "372bdb17", + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "def op(command):\n", + " match command:\n", + " case ['move', *directions]:\n", + " return tuple(symbols[direction] for direction in directions)\n", + " case \"pick\":\n", + " return symbols[\"pick\"]\n", + " case \"drop\":\n", + " return symbols[\"drop\"]\n", + " case _:\n", + " raise ValueError(f\"{command} does not compute!\")" + ] + }, + { + "cell_type": "markdown", + "id": "c77bec44", + "metadata": {}, + "source": [ + "What happens here is that the pattern matcher will recognize the first word `move` and then interpret the remaining words collection them in the `directions` variable (so this syntax is very similar to unpacking)." + ] + }, + { + "cell_type": "markdown", + "id": "4ef78058", + "metadata": {}, + "source": [ + "We can now rewrite our program this way:" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "88c0c024", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[('→', '→', '↑'), '⤣', ('↓', '↑', '→'), '⤥']" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "[\n", + " op([\"move\", \"F\", \"F\", \"L\"]),\n", + " op(\"pick\"),\n", + " op([\"move\", \"R\", \"L\", \"F\"]),\n", + " op(\"drop\"),\n", + "]" + ] + }, + { + "cell_type": "markdown", + "id": "1ddc0f2e", + "metadata": {}, + "source": [ + "But now we have a slight problem:" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "ed30a762", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " 'up'\n" + ] + } + ], + "source": [ + "try:\n", + " op([\"move\", \"up\"])\n", + "except Exception as ex:\n", + " print(type(ex), ex)" + ] + }, + { + "cell_type": "markdown", + "id": "f6bbbd53", + "metadata": {}, + "source": [ + "We would rather just get our custom `ValueError`. To do this we can place a **guard** on our `case` for the `move` command, that will not only do the match but also test an additional condition:" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "8d441175", + "metadata": {}, + "outputs": [], + "source": [ + "def op(command):\n", + " match command:\n", + " case ['move', *directions] if set(directions) < symbols.keys():\n", + " return tuple(symbols[direction] for direction in directions)\n", + " case \"pick\":\n", + " return symbols[\"pick\"]\n", + " case \"drop\":\n", + " return symbols[\"drop\"]\n", + " case _:\n", + " raise ValueError(f\"{command} does not compute!\")" + ] + }, + { + "cell_type": "markdown", + "id": "441a53f6", + "metadata": {}, + "source": [ + "That `if ` statement (the **guard**) will only let the case block execute if the match is true **and** that `if` expression evaludates to `True`:" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "708c5d16", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " ['move', 'up'] does not compute!\n" + ] + } + ], + "source": [ + "try:\n", + " op([\"move\", \"up\"])\n", + "except Exception as ex:\n", + " print(type(ex), ex)" + ] + }, + { + "cell_type": "markdown", + "id": "1ecf5f83", + "metadata": {}, + "source": [ + "There are many other ways we could have done this - probably better than this, but this was to illustrate how the multiple value matching can work!" + ] + }, + { + "cell_type": "markdown", + "id": "84bec678", + "metadata": {}, + "source": [ + "I urge you to read at least this [tutorial (pep 636)](https://peps.python.org/pep-0636/) on pattern matching." + ] + }, + { + "cell_type": "markdown", + "id": "5b6a7b09", + "metadata": {}, + "source": [ + "#### The `zip` Function" + ] + }, + { + "cell_type": "markdown", + "id": "1294e5f6", + "metadata": {}, + "source": [ + "We use the built-in `zip` function all the time. As we know, the `zip` will stop iterating after the first of the iterables provided in the arguments is exhausted:" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "e33aa60d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[('a', 10), ('b', 20), ('c', 30)]" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "l1 = ['a', 'b', 'c']\n", + "l2 = [10, 20, 30, 40]\n", + "\n", + "list(zip(l1, l2))" + ] + }, + { + "cell_type": "markdown", + "id": "7d310af8", + "metadata": {}, + "source": [ + "As you can see the last element of `l2` is not included in the result since `l1` only had three elements." + ] + }, + { + "cell_type": "markdown", + "id": "a9cf9570", + "metadata": {}, + "source": [ + "We can also use the `zip_longest` function in the `itertools` module to iterate over the longest iterable, providing some default value for any other iterable that has been exhausted:" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "aefa3055", + "metadata": {}, + "outputs": [], + "source": [ + "from itertools import zip_longest" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "ce4779c9", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[('a', 10), ('b', 20), ('c', 30), ('???', 40)]" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list(zip_longest(l1, l2, fillvalue='???'))" + ] + }, + { + "cell_type": "markdown", + "id": "0eb2f702", + "metadata": {}, + "source": [ + "But what if we want to only zip iterables that have the **same** length? We would need to test the length of each iterable first - but if those were iterators instead of iterables, we will have exhausted the iterator, and `zip` would come back \"empty\":" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "3b704410", + "metadata": {}, + "outputs": [], + "source": [ + "l1 = (i ** 2 for i in range(4))\n", + "l2 = (i ** 3 for i in range(3))" + ] + }, + { + "cell_type": "markdown", + "id": "da0d866f", + "metadata": {}, + "source": [ + "We could test to see if `l1` and `l2` are the same length:" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "bc7d34c9", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "len(list(l1)) == len(list(l2))" + ] + }, + { + "cell_type": "markdown", + "id": "0a4a1974", + "metadata": {}, + "source": [ + "But, if we now try to `zip` them:" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "1c3928b9", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[]" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list(zip(l1, l2))" + ] + }, + { + "cell_type": "markdown", + "id": "9ee310e9", + "metadata": {}, + "source": [ + "In Python 3.10, the `zip` function now has a keyword-only parameter called `strict` that will just do the zip, but throw an exception if one of the arguments get exhausted before the others:" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "54722489", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "zip() argument 2 is shorter than argument 1\n" + ] + } + ], + "source": [ + "l1 = (i ** 2 for i in range(4))\n", + "l2 = (i ** 3 for i in range(3))\n", + "\n", + "try:\n", + " list(zip(l1, l2, strict=True))\n", + "except ValueError as ex:\n", + " print(ex)" + ] + }, + { + "cell_type": "markdown", + "id": "2635211a", + "metadata": {}, + "source": [ + "And works just fine if the arguments all have the same length:" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "7fb672cb", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[(0, 0, 0), (1, 1, 1), (4, 8, 16), (9, 27, 81)]" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "l1 = (i ** 2 for i in range(4))\n", + "l2 = (i ** 3 for i in range(4))\n", + "l3 = (i ** 4 for i in range(4))\n", + "\n", + "list(zip(l1, l2, l3))" + ] + }, + { + "cell_type": "markdown", + "id": "501fec3b", + "metadata": {}, + "source": [ + "So why is this useful?" + ] + }, + { + "cell_type": "markdown", + "id": "dab4260e", + "metadata": {}, + "source": [ + "In **many** cases, our code zips iterables that we expect to be of the same length. To avoid bugs in our program, we should check that this condition is true, otherwise zip will silently just zip based on the shortest one. But as we saw with iterators, that can be difficult to do without exhausting the very iterators we are trying to zip. (it can be done, it's just more code)." + ] + }, + { + "cell_type": "markdown", + "id": "c73e5ad1", + "metadata": {}, + "source": [ + "So, if you are one of the lucky devs that gets to write Python 3.10 (or higher :-) ) code, you can just use `strict` whenever you zip things together and expect that they are all of the same length. Much easier to do it this way (and, as we discuss in Exception handling, falls into the category of \"ask forgiveness later\" which we saw was the preferred way (in general) to handle exceptions in our apps, as opposed to the \"look before you leap\" approach we would have to use to test the argument lengths." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "400a56c1", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.10.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Part 3/Section 11 - Extras/01 - Relevant Python 3.8 Changes.ipynb b/Part 3/Section 11 - Extras/01 - Relevant Python 3.8 Changes.ipynb new file mode 100644 index 00000000..699f8626 --- /dev/null +++ b/Part 3/Section 11 - Extras/01 - Relevant Python 3.8 Changes.ipynb @@ -0,0 +1,1476 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "c314393b", + "metadata": {}, + "source": [ + "### Relevant Python 3.8 Changes" + ] + }, + { + "cell_type": "markdown", + "id": "cbe45497", + "metadata": {}, + "source": [ + "The release of Python 3.8 has brought some new features.\n", + "\n", + "This is a summary of the ones _I_ deemed relevant to this course, and does **not** include all the changes!\n", + "\n", + "For full release details, see [here](https://docs.python.org/3/whatsnew/3.8.html)" + ] + }, + { + "cell_type": "markdown", + "id": "7089277d", + "metadata": {}, + "source": [ + "#### Positional Only Parameters" + ] + }, + { + "cell_type": "markdown", + "id": "61154c87", + "metadata": {}, + "source": [ + "It is now possible to define **positional-only** parameters for Python functions." + ] + }, + { + "cell_type": "markdown", + "id": "63037bda", + "metadata": {}, + "source": [ + "As we saw earlier in this course, when you define positional parameters in a function:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "13c1ba59", + "metadata": {}, + "outputs": [], + "source": [ + "def my_func(a, b):\n", + " return a + b" + ] + }, + { + "cell_type": "markdown", + "id": "a4928079", + "metadata": {}, + "source": [ + "the user is free to pass arguments either positionally:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "0bb6fea5", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "my_func(1, 2)" + ] + }, + { + "cell_type": "markdown", + "id": "be1cfa90", + "metadata": {}, + "source": [ + "or, as named arguments:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "cb04e54f", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "my_func(b=2, a=1)" + ] + }, + { + "cell_type": "markdown", + "id": "617077dd", + "metadata": {}, + "source": [ + "Some functions in Python's built-ins ared defined in such a way that certain parameters can **only** be passed positionally, for example the `print` function:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "77c6fa8a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Help on built-in function print in module builtins:\n", + "\n", + "print(...)\n", + " print(value, ..., sep=' ', end='\\n', file=sys.stdout, flush=False)\n", + " \n", + " Prints the values to a stream, or to sys.stdout by default.\n", + " Optional keyword arguments:\n", + " file: a file-like object (stream); defaults to the current sys.stdout.\n", + " sep: string inserted between values, default a space.\n", + " end: string appended after the last value, default a newline.\n", + " flush: whether to forcibly flush the stream.\n", + "\n" + ] + } + ], + "source": [ + "help(print)" + ] + }, + { + "cell_type": "markdown", + "id": "248c2581", + "metadata": {}, + "source": [ + "That `value` cannot be passed by name:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "529c5dac", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "'value' is an invalid keyword argument for print()\n" + ] + } + ], + "source": [ + "try:\n", + " print(value=\"hello\")\n", + "except TypeError as ex:\n", + " print(ex)" + ] + }, + { + "cell_type": "markdown", + "id": "f4baed67", + "metadata": {}, + "source": [ + "Instead, the parameter **must** be passed positionally:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "8c147557", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "hello\n" + ] + } + ], + "source": [ + "print(\"hello\")" + ] + }, + { + "cell_type": "markdown", + "id": "d2840374", + "metadata": {}, + "source": [ + "Until Python 3.8, it was not possible to reproduce such behavior with user-defined functions." + ] + }, + { + "cell_type": "markdown", + "id": "158674a9", + "metadata": {}, + "source": [ + "Now you can, by using the slash character(`/`). Parameters defined **before** the `/` become **positional-only** parameters:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "aeebd0f8", + "metadata": {}, + "outputs": [], + "source": [ + "def my_func(a, b, /):\n", + " return a + b" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "858727b6", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "my_func(1, 2)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "4f73c741", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "my_func() got some positional-only arguments passed as keyword arguments: 'a, b'\n" + ] + } + ], + "source": [ + "try:\n", + " my_func(a=1, b=2)\n", + "except TypeError as ex:\n", + " print(ex)" + ] + }, + { + "cell_type": "markdown", + "id": "f5967700", + "metadata": {}, + "source": [ + "You can of course mix this along with the special parameters `*` and `**`:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "d17254b3", + "metadata": {}, + "outputs": [], + "source": [ + "def my_func(a, b, /, *, c):\n", + " print(a + b + c)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "b35d884e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "13\n" + ] + } + ], + "source": [ + "my_func(1, 2, c=10)" + ] + }, + { + "cell_type": "markdown", + "id": "573aca30", + "metadata": {}, + "source": [ + "#### f-string Enhancements" + ] + }, + { + "cell_type": "markdown", + "id": "89ee51f9", + "metadata": {}, + "source": [ + "Often we use f-strings to interpolate the name of a variable and it's value:" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "f79a630b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "a=hello, b=world\n" + ] + } + ], + "source": [ + "a, b = \"hello\", \"world\"\n", + "print(f\"a={a}, b={b}\")" + ] + }, + { + "cell_type": "markdown", + "id": "24747607", + "metadata": {}, + "source": [ + "Python 3.8 provides a shortcut way of doing the same thing:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "3ae5c0c4", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "a='hello', b='world'\n" + ] + } + ], + "source": [ + "print(f\"{a=}, {b=}\")" + ] + }, + { + "cell_type": "markdown", + "id": "141558b4", + "metadata": {}, + "source": [ + "You can even use [format specifiers](https://docs.python.org/3/library/string.html#formatspec)\n", + "to further customize the output:" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "e75716ac", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "a=hello, b=world\n" + ] + } + ], + "source": [ + "print(f\"{a=:s}, {b=:s}\")" + ] + }, + { + "cell_type": "markdown", + "id": "4dcdc5d0", + "metadata": {}, + "source": [ + "Or when dealing with other types:" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "362bf6b5", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import datetime\n", + "from math import pi" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "c91b7536", + "metadata": {}, + "outputs": [], + "source": [ + "d = datetime.utcnow()\n", + "e = pi" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "545c77a2", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "d=datetime.datetime(2022, 3, 20, 6, 1, 13, 990493), e=3.141592653589793\n" + ] + } + ], + "source": [ + "print(f\"{d=}, {e=}\")" + ] + }, + { + "cell_type": "markdown", + "id": "4637ecd5", + "metadata": {}, + "source": [ + "And applying some format specifiers:" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "75c4052b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "d=2022-03-20 06:01:13, e=3.142\n" + ] + } + ], + "source": [ + "print(f\"{d=:%Y-%m-%d %H:%M:%S}, {e=:.3f}\")" + ] + }, + { + "cell_type": "markdown", + "id": "076773ed", + "metadata": {}, + "source": [ + "It will even display the text of an expression if you use one in your f-string:" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "0ce46ace", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1 + 2=3, ' '.join(sentence)='Python rocks!'\n" + ] + } + ], + "source": [ + "sentence = [\"Python\", \"rocks!\"]\n", + "print(f\"{1 + 2=}, {' '.join(sentence)=}\")" + ] + }, + { + "cell_type": "markdown", + "id": "9e3783ea", + "metadata": {}, + "source": [ + "#### The `as_integer_ratio()` Method" + ] + }, + { + "cell_type": "markdown", + "id": "15ccd5ad", + "metadata": {}, + "source": [ + "The types `bool`, `int` and `Fraction` now all implement an `as_integer_ratio()` method which returns a tuple consisting of the numerator and denominator. Remember that `Decimal` and `float` already implement the same method." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "8313fc80", + "metadata": {}, + "outputs": [], + "source": [ + "from fractions import Fraction" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "7a1ae84a", + "metadata": {}, + "outputs": [], + "source": [ + "f = Fraction(2, 3)" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "a94fc3ed", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(2, 3)" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "f.as_integer_ratio()" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "f32942ec", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(12, 1)" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a = 12\n", + "a.as_integer_ratio()" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "06980a8c", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(1, 1)" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "flag = True\n", + "flag.as_integer_ratio()" + ] + }, + { + "cell_type": "markdown", + "id": "a3091e0e", + "metadata": {}, + "source": [ + "The advantage of this is mainly for polymorphism (or duck-typing), where you can now use `as_integer_ratio` irrespective of whether the variable is a `bool`, an `int`, a `float`, a `Decimal` or a `Fraction`." + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "8a362bf8", + "metadata": {}, + "outputs": [], + "source": [ + "from decimal import Decimal" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "ab4a78aa", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(33, 100)" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Decimal(\"0.33\").as_integer_ratio()" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "5ee29a47", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(7070651414971679, 2251799813685248)" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(3.14).as_integer_ratio()" + ] + }, + { + "cell_type": "markdown", + "id": "df1e85de", + "metadata": {}, + "source": [ + "#### The `lru_cache` decorator" + ] + }, + { + "cell_type": "markdown", + "id": "226efb47", + "metadata": {}, + "source": [ + "As we saw in this course, we can use the `lru_cache` decorator to appky an LRU cache to our functions:" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "20501526", + "metadata": {}, + "outputs": [], + "source": [ + "from functools import lru_cache" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "07d699f0", + "metadata": {}, + "outputs": [], + "source": [ + "@lru_cache(maxsize=3)\n", + "def fib(n):\n", + " if n <=2 :\n", + " return 1\n", + " else:\n", + " return fib(n-1) + fib(n-2)" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "45a0cd5a", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "354224848179261915075" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "fib(100)" + ] + }, + { + "cell_type": "markdown", + "id": "fea747d4", + "metadata": {}, + "source": [ + "If we don't specify `maxsize`, it will default to `128`:" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "id": "447be276", + "metadata": {}, + "outputs": [], + "source": [ + "@lru_cache()\n", + "def fib(n):\n", + " if n <=2 :\n", + " return 1\n", + " else:\n", + " return fib(n-1) + fib(n-2)" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "id": "53fee4fd", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "354224848179261915075" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "fib(100)" + ] + }, + { + "cell_type": "markdown", + "id": "5cf8e33a", + "metadata": {}, + "source": [ + "The change made to this decorator in Python 3.8 allows us not to use those empty parentheses:" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "id": "4185658b", + "metadata": {}, + "outputs": [], + "source": [ + "@lru_cache\n", + "def fib(n):\n", + " if n <=2 :\n", + " return 1\n", + " else:\n", + " return fib(n-1) + fib(n-2)" + ] + }, + { + "cell_type": "markdown", + "id": "76a9aed5", + "metadata": {}, + "source": [ + "#### `math` Module" + ] + }, + { + "cell_type": "markdown", + "id": "0af410c0", + "metadata": {}, + "source": [ + "Many examples I use throughout this course calculate the Euclidean distance between two points:" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "id": "b511addc", + "metadata": {}, + "outputs": [], + "source": [ + "import math" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "id": "0b66c006", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1.4142135623730951\n" + ] + } + ], + "source": [ + "a = (0, 0)\n", + "b = (1, 1)\n", + "\n", + "dist = math.sqrt((b[0] - a[1]) ** 2 + (b[1] - a[1]) ** 2)\n", + "print(dist)" + ] + }, + { + "cell_type": "markdown", + "id": "d276589d", + "metadata": {}, + "source": [ + "Now, it's much easier using the `dist()` function the `math` module:" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "id": "1f774757", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "1.4142135623730951" + ] + }, + "execution_count": 36, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "math.dist(a, b)" + ] + }, + { + "cell_type": "markdown", + "id": "13afb1f1", + "metadata": {}, + "source": [ + "#### The `namedtuple` Implementation" + ] + }, + { + "cell_type": "markdown", + "id": "a22a839e", + "metadata": {}, + "source": [ + "Actually these changes were added to Python 3.7, but since I don't have a separate lecture for Python 3.7 changes (most did not apply to this course), here it is." + ] + }, + { + "cell_type": "markdown", + "id": "1a579fbb", + "metadata": {}, + "source": [ + "The `_source` attribute was **removed**. There quite a discussion on this, and the the core dev who implemented and supported this essentially gave up trying to keep this in - it was deemed to cause too much \"overhead\". So, sadly (wearing my teacher's hat), it is gone. It is no more. It's not pining, it's just dead. :-)" + ] + }, + { + "cell_type": "markdown", + "id": "d5e3bfd0", + "metadata": {}, + "source": [ + "The method I showed you for defining defaults for named tuples still works, and could still be used, but Python 3.7 added the `defaults` parameter to the named tuple definition." + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "id": "3538a2c2", + "metadata": {}, + "outputs": [], + "source": [ + "from collections import namedtuple" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "id": "024bcc51", + "metadata": {}, + "outputs": [], + "source": [ + "NT = namedtuple(\"NT\", \"a b c\", defaults=(10, 20, 30))" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "id": "9dd8a877", + "metadata": {}, + "outputs": [], + "source": [ + "nt = NT()" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "id": "db7f47e7", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "NT(a=10, b=20, c=30)" + ] + }, + "execution_count": 40, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nt" + ] + }, + { + "cell_type": "markdown", + "id": "fb0bc811", + "metadata": {}, + "source": [ + "You don't have to specify defaults for everything, but if you do not, be aware that defaults will be applied from **right** to **left**. Which makes sense given that in Python non-defaulted parameters must be defined **before** defaulted parameters." + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "id": "8f6ad2ca", + "metadata": {}, + "outputs": [], + "source": [ + "NT = namedtuple(\"NT\", \"a b c\", defaults = (20, 30))" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "id": "bc9d5345", + "metadata": {}, + "outputs": [], + "source": [ + "nt = NT(10)" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "id": "eceeaec2", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "NT(a=10, b=20, c=30)" + ] + }, + "execution_count": 43, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nt" + ] + }, + { + "cell_type": "markdown", + "id": "64188b1c", + "metadata": {}, + "source": [ + "Note that with this way of specifying defaults you can easily define the same default for all items in the named tuple using the `*` operator:" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "id": "8b36d7c3", + "metadata": {}, + "outputs": [], + "source": [ + "NT = namedtuple(\"NT\", \"a b c d e f\", defaults=(\"xyz\",) * 6)" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "id": "1d448806", + "metadata": {}, + "outputs": [], + "source": [ + "nt = NT()" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "id": "b16758de", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "NT(a='xyz', b='xyz', c='xyz', d='xyz', e='xyz', f='xyz')" + ] + }, + "execution_count": 46, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nt" + ] + }, + { + "cell_type": "markdown", + "id": "bbcbaf53", + "metadata": {}, + "source": [ + "Just be careful if you use a **mutable** type to do this!!" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "id": "91b28470", + "metadata": {}, + "outputs": [], + "source": [ + "NT = namedtuple(\"NT\", \"a b c\", defaults = ([],) * 3)" + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "id": "cad11f5c", + "metadata": {}, + "outputs": [], + "source": [ + "nt = NT()" + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "id": "0cb7ceac", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "NT(a=[], b=[], c=[])" + ] + }, + "execution_count": 49, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nt" + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "id": "466d9520", + "metadata": {}, + "outputs": [], + "source": [ + "nt.a.append(10)" + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "id": "74826099", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[10]" + ] + }, + "execution_count": 51, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nt.a" + ] + }, + { + "cell_type": "markdown", + "id": "6d00b122", + "metadata": {}, + "source": [ + "But watch this!" + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "id": "4759e6f4", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "NT(a=[10], b=[10], c=[10])" + ] + }, + "execution_count": 52, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nt" + ] + }, + { + "cell_type": "markdown", + "id": "972c666e", + "metadata": {}, + "source": [ + "I hope you understand what happened here without me telling you!" + ] + }, + { + "cell_type": "markdown", + "id": "6590d73a", + "metadata": {}, + "source": [ + "The **same** list object was re-used 3 times in the defaults." + ] + }, + { + "cell_type": "markdown", + "id": "720e13bb", + "metadata": {}, + "source": [ + "You can easily recover your defaults using the `_field_defaults` method:" + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "id": "586498ae", + "metadata": {}, + "outputs": [], + "source": [ + "NT = namedtuple(\"NT\", \"a, b, c\", defaults=(1, 2, 3))" + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "id": "20f4012e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'a': 1, 'b': 2, 'c': 3}" + ] + }, + "execution_count": 54, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "NT._field_defaults" + ] + }, + { + "cell_type": "markdown", + "id": "70c3a717", + "metadata": {}, + "source": [ + "One change of note in Python 3.8, the `_as_dict()` method now returns a standard dictionary (key ordered in the same way as the named tuple). Prior to this version, it would return an `OrderedDict` since standard Python dictionaries did not guarantee any specific key order, but since they now do, there's no need to use the `DefaultDict` anymore." + ] + }, + { + "cell_type": "markdown", + "id": "ecb5b3a0", + "metadata": {}, + "source": [ + "#### Other Things" + ] + }, + { + "cell_type": "markdown", + "id": "9d8ec24b", + "metadata": {}, + "source": [ + "These are few other odds and ends that you might find of interest:" + ] + }, + { + "cell_type": "markdown", + "id": "3602bbab", + "metadata": {}, + "source": [ + "The built-in `reversed` function now works with dictionary views:" + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "id": "4f68e46b", + "metadata": {}, + "outputs": [], + "source": [ + "d = {'a': 1, 'b': 2}" + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "id": "83fc356d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['a', 'b']" + ] + }, + "execution_count": 56, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list(d.keys())" + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "id": "10648abb", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['b', 'a']" + ] + }, + "execution_count": 57, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list(reversed(d.keys()))" + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "id": "4a4885c3", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[2, 1]" + ] + }, + "execution_count": 58, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list(reversed(d.values()))" + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "id": "13785eb9", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[('b', 2), ('a', 1)]" + ] + }, + "execution_count": 59, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list(reversed(d.items()))" + ] + }, + { + "cell_type": "markdown", + "id": "e14d5c14", + "metadata": {}, + "source": [ + "The `continue` statement was not permitted in the `finally` clause of a loop, but is now supported." + ] + }, + { + "cell_type": "markdown", + "id": "fe507127", + "metadata": {}, + "source": [ + "Earlier in Part 1, we discussed string interning, as well as how a small selection of integers are essentially \"cached\" by Python and re-used whenever the literal is encountered. This meant we could use `is` instead of `==` in some cases, and that helped us get a clearer understanding of what's going on. **BUT**, as we also discussed, you should **not**, in practice, use `is` for comparing objects such as integers, strings, etc (usually we are more interested in whether is the same value, rather than the identical object) - the fact that this works is an implementation detail and not guaranteed to work the same way from one Python version to another.\n", + "\n", + "Although linters will usually catch those kinds of issues, not everyone uses a Python linter - so Python 3.8 will now emit a **warning** if you compare variables of certain types with literal constants!" + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "id": "e659956e", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "<>:2: SyntaxWarning: \"is\" with a literal. Did you mean \"==\"?\n", + "<>:2: SyntaxWarning: \"is\" with a literal. Did you mean \"==\"?\n", + "/var/folders/kg/20pf1n8j6pjg09bwxz6cgw0r0000gn/T/ipykernel_33116/3780910533.py:2: SyntaxWarning: \"is\" with a literal. Did you mean \"==\"?\n", + " a is 1\n" + ] + }, + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 60, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a = 1\n", + "a is 1" + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "id": "d970904d", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "<>:2: SyntaxWarning: \"is\" with a literal. Did you mean \"==\"?\n", + "<>:2: SyntaxWarning: \"is\" with a literal. Did you mean \"==\"?\n", + "/var/folders/kg/20pf1n8j6pjg09bwxz6cgw0r0000gn/T/ipykernel_33116/4272392880.py:2: SyntaxWarning: \"is\" with a literal. Did you mean \"==\"?\n", + " a is 'hello'\n" + ] + }, + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 61, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a = 'hello'\n", + "a is 'hello'" + ] + }, + { + "cell_type": "markdown", + "id": "01c01a49", + "metadata": {}, + "source": [ + "But we do not get a warning in a situation such as this:" + ] + }, + { + "cell_type": "code", + "execution_count": 62, + "id": "c0d535bc", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 62, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a = [1, 2, 3]\n", + "\n", + "a is [1, 2, 3]" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.10.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Part 3/Section 11 - Extras/02 - Relevant Python 3.9 Changes.ipynb b/Part 3/Section 11 - Extras/02 - Relevant Python 3.9 Changes.ipynb new file mode 100644 index 00000000..698dadce --- /dev/null +++ b/Part 3/Section 11 - Extras/02 - Relevant Python 3.9 Changes.ipynb @@ -0,0 +1,2343 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "c45f0998", + "metadata": {}, + "source": [ + "### Relevant Python 3.9 Changes" + ] + }, + { + "cell_type": "markdown", + "id": "2e965eba", + "metadata": {}, + "source": [ + "The release of Python 3.9 has brought some new features.\n", + "\n", + "This is a summary of the ones _I_ deemed relevant to this course, and does **not** include all the changes!\n", + "\n", + "For full release details, see [here](https://docs.python.org/3/whatsnew/3.9.html)" + ] + }, + { + "cell_type": "markdown", + "id": "39255227", + "metadata": {}, + "source": [ + "#### Time Zones" + ] + }, + { + "cell_type": "markdown", + "id": "26750adb", + "metadata": {}, + "source": [ + "We don't cover 3rd party libraries in this course, but if you've worked with Python in a production environment, you will likely have come across the dreaded timezone and Daylight Savings issues that plague datetimes!" + ] + }, + { + "cell_type": "markdown", + "id": "7cdfe8a5", + "metadata": {}, + "source": [ + "Most likely you will have resorted to using the `pytz` and `python-dateutil` libraries to help with that." + ] + }, + { + "cell_type": "markdown", + "id": "bc000883", + "metadata": {}, + "source": [ + "Now, Python 3.9 is proud to introduce the `zoneinfo` module to deal with timezones properly. About time too!" + ] + }, + { + "cell_type": "markdown", + "id": "c2d652dd", + "metadata": {}, + "source": [ + "For full info on this, refer to [PEP 615](https://peps.python.org/pep-0615/).\n", + "\n", + "And the Python [docs](https://docs.python.org/3.9/library/zoneinfo.html#module-zoneinfo).\n", + "\n", + "**Windows Users**: you will likely need to add a dependency on the `tzdata` [library](https://pypi.org/project/tzdata/) for the IANA time zone database. See [this note](https://docs.python.org/3.9/library/zoneinfo.html#data-sources)\n", + "\n", + "You should also take a look at this [presentation](https://pganssle-talks.github.io/chipy-nov-2020-zoneinfo/#/) by Paul Ganssle who wrote that module - very interesting read!" + ] + }, + { + "cell_type": "markdown", + "id": "80255944", + "metadata": {}, + "source": [ + "Let's look at how we might have handled timezone and DST using `pytz` and `dateutil`, and contrast that to how we can use the new `zoneinfo` module instead." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "ec129288", + "metadata": {}, + "outputs": [], + "source": [ + "import zoneinfo\n", + "from datetime import datetime, timezone\n", + "from zoneinfo import ZoneInfo\n", + "\n", + "import dateutil\n", + "import pytz" + ] + }, + { + "cell_type": "markdown", + "id": "beadc4e2", + "metadata": {}, + "source": [ + "Let's list out all the defined time zones:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "d0058e73", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Africa/Abidjan\n", + "Africa/Accra\n", + "Africa/Addis_Ababa\n", + "Africa/Algiers\n", + "Africa/Asmara\n", + "Africa/Asmera\n", + "Africa/Bamako\n", + "Africa/Bangui\n", + "Africa/Banjul\n", + "Africa/Bissau\n", + "Africa/Blantyre\n", + "Africa/Brazzaville\n", + "Africa/Bujumbura\n", + "Africa/Cairo\n", + "Africa/Casablanca\n", + "Africa/Ceuta\n", + "Africa/Conakry\n", + "Africa/Dakar\n", + "Africa/Dar_es_Salaam\n", + "Africa/Djibouti\n", + "Africa/Douala\n", + "Africa/El_Aaiun\n", + "Africa/Freetown\n", + "Africa/Gaborone\n", + "Africa/Harare\n", + "Africa/Johannesburg\n", + "Africa/Juba\n", + "Africa/Kampala\n", + "Africa/Khartoum\n", + "Africa/Kigali\n", + "Africa/Kinshasa\n", + "Africa/Lagos\n", + "Africa/Libreville\n", + "Africa/Lome\n", + "Africa/Luanda\n", + "Africa/Lubumbashi\n", + "Africa/Lusaka\n", + "Africa/Malabo\n", + "Africa/Maputo\n", + "Africa/Maseru\n", + "Africa/Mbabane\n", + "Africa/Mogadishu\n", + "Africa/Monrovia\n", + "Africa/Nairobi\n", + "Africa/Ndjamena\n", + "Africa/Niamey\n", + "Africa/Nouakchott\n", + "Africa/Ouagadougou\n", + "Africa/Porto-Novo\n", + "Africa/Sao_Tome\n", + "Africa/Timbuktu\n", + "Africa/Tripoli\n", + "Africa/Tunis\n", + "Africa/Windhoek\n", + "America/Adak\n", + "America/Anchorage\n", + "America/Anguilla\n", + "America/Antigua\n", + "America/Araguaina\n", + "America/Argentina/Buenos_Aires\n", + "America/Argentina/Catamarca\n", + "America/Argentina/ComodRivadavia\n", + "America/Argentina/Cordoba\n", + "America/Argentina/Jujuy\n", + "America/Argentina/La_Rioja\n", + "America/Argentina/Mendoza\n", + "America/Argentina/Rio_Gallegos\n", + "America/Argentina/Salta\n", + "America/Argentina/San_Juan\n", + "America/Argentina/San_Luis\n", + "America/Argentina/Tucuman\n", + "America/Argentina/Ushuaia\n", + "America/Aruba\n", + "America/Asuncion\n", + "America/Atikokan\n", + "America/Atka\n", + "America/Bahia\n", + "America/Bahia_Banderas\n", + "America/Barbados\n", + "America/Belem\n", + "America/Belize\n", + "America/Blanc-Sablon\n", + "America/Boa_Vista\n", + "America/Bogota\n", + "America/Boise\n", + "America/Buenos_Aires\n", + "America/Cambridge_Bay\n", + "America/Campo_Grande\n", + "America/Cancun\n", + "America/Caracas\n", + "America/Catamarca\n", + "America/Cayenne\n", + "America/Cayman\n", + "America/Chicago\n", + "America/Chihuahua\n", + "America/Coral_Harbour\n", + "America/Cordoba\n", + "America/Costa_Rica\n", + "America/Creston\n", + "America/Cuiaba\n", + "America/Curacao\n", + "America/Danmarkshavn\n", + "America/Dawson\n", + "America/Dawson_Creek\n", + "America/Denver\n", + "America/Detroit\n", + "America/Dominica\n", + "America/Edmonton\n", + "America/Eirunepe\n", + "America/El_Salvador\n", + "America/Ensenada\n", + "America/Fort_Nelson\n", + "America/Fort_Wayne\n", + "America/Fortaleza\n", + "America/Glace_Bay\n", + "America/Godthab\n", + "America/Goose_Bay\n", + "America/Grand_Turk\n", + "America/Grenada\n", + "America/Guadeloupe\n", + "America/Guatemala\n", + "America/Guayaquil\n", + "America/Guyana\n", + "America/Halifax\n", + "America/Havana\n", + "America/Hermosillo\n", + "America/Indiana/Indianapolis\n", + "America/Indiana/Knox\n", + "America/Indiana/Marengo\n", + "America/Indiana/Petersburg\n", + "America/Indiana/Tell_City\n", + "America/Indiana/Vevay\n", + "America/Indiana/Vincennes\n", + "America/Indiana/Winamac\n", + "America/Indianapolis\n", + "America/Inuvik\n", + "America/Iqaluit\n", + "America/Jamaica\n", + "America/Jujuy\n", + "America/Juneau\n", + "America/Kentucky/Louisville\n", + "America/Kentucky/Monticello\n", + "America/Knox_IN\n", + "America/Kralendijk\n", + "America/La_Paz\n", + "America/Lima\n", + "America/Los_Angeles\n", + "America/Louisville\n", + "America/Lower_Princes\n", + "America/Maceio\n", + "America/Managua\n", + "America/Manaus\n", + "America/Marigot\n", + "America/Martinique\n", + "America/Matamoros\n", + "America/Mazatlan\n", + "America/Mendoza\n", + "America/Menominee\n", + "America/Merida\n", + "America/Metlakatla\n", + "America/Mexico_City\n", + "America/Miquelon\n", + "America/Moncton\n", + "America/Monterrey\n", + "America/Montevideo\n", + "America/Montreal\n", + "America/Montserrat\n", + "America/Nassau\n", + "America/New_York\n", + "America/Nipigon\n", + "America/Nome\n", + "America/Noronha\n", + "America/North_Dakota/Beulah\n", + "America/North_Dakota/Center\n", + "America/North_Dakota/New_Salem\n", + "America/Nuuk\n", + "America/Ojinaga\n", + "America/Panama\n", + "America/Pangnirtung\n", + "America/Paramaribo\n", + "America/Phoenix\n", + "America/Port-au-Prince\n", + "America/Port_of_Spain\n", + "America/Porto_Acre\n", + "America/Porto_Velho\n", + "America/Puerto_Rico\n", + "America/Punta_Arenas\n", + "America/Rainy_River\n", + "America/Rankin_Inlet\n", + "America/Recife\n", + "America/Regina\n", + "America/Resolute\n", + "America/Rio_Branco\n", + "America/Rosario\n", + "America/Santa_Isabel\n", + "America/Santarem\n", + "America/Santiago\n", + "America/Santo_Domingo\n", + "America/Sao_Paulo\n", + "America/Scoresbysund\n", + "America/Shiprock\n", + "America/Sitka\n", + "America/St_Barthelemy\n", + "America/St_Johns\n", + "America/St_Kitts\n", + "America/St_Lucia\n", + "America/St_Thomas\n", + "America/St_Vincent\n", + "America/Swift_Current\n", + "America/Tegucigalpa\n", + "America/Thule\n", + "America/Thunder_Bay\n", + "America/Tijuana\n", + "America/Toronto\n", + "America/Tortola\n", + "America/Vancouver\n", + "America/Virgin\n", + "America/Whitehorse\n", + "America/Winnipeg\n", + "America/Yakutat\n", + "America/Yellowknife\n", + "Antarctica/Casey\n", + "Antarctica/Davis\n", + "Antarctica/DumontDUrville\n", + "Antarctica/Macquarie\n", + "Antarctica/Mawson\n", + "Antarctica/McMurdo\n", + "Antarctica/Palmer\n", + "Antarctica/Rothera\n", + "Antarctica/South_Pole\n", + "Antarctica/Syowa\n", + "Antarctica/Troll\n", + "Antarctica/Vostok\n", + "Arctic/Longyearbyen\n", + "Asia/Aden\n", + "Asia/Almaty\n", + "Asia/Amman\n", + "Asia/Anadyr\n", + "Asia/Aqtau\n", + "Asia/Aqtobe\n", + "Asia/Ashgabat\n", + "Asia/Ashkhabad\n", + "Asia/Atyrau\n", + "Asia/Baghdad\n", + "Asia/Bahrain\n", + "Asia/Baku\n", + "Asia/Bangkok\n", + "Asia/Barnaul\n", + "Asia/Beirut\n", + "Asia/Bishkek\n", + "Asia/Brunei\n", + "Asia/Calcutta\n", + "Asia/Chita\n", + "Asia/Choibalsan\n", + "Asia/Chongqing\n", + "Asia/Chungking\n", + "Asia/Colombo\n", + "Asia/Dacca\n", + "Asia/Damascus\n", + "Asia/Dhaka\n", + "Asia/Dili\n", + "Asia/Dubai\n", + "Asia/Dushanbe\n", + "Asia/Famagusta\n", + "Asia/Gaza\n", + "Asia/Harbin\n", + "Asia/Hebron\n", + "Asia/Ho_Chi_Minh\n", + "Asia/Hong_Kong\n", + "Asia/Hovd\n", + "Asia/Irkutsk\n", + "Asia/Istanbul\n", + "Asia/Jakarta\n", + "Asia/Jayapura\n", + "Asia/Jerusalem\n", + "Asia/Kabul\n", + "Asia/Kamchatka\n", + "Asia/Karachi\n", + "Asia/Kashgar\n", + "Asia/Kathmandu\n", + "Asia/Katmandu\n", + "Asia/Khandyga\n", + "Asia/Kolkata\n", + "Asia/Krasnoyarsk\n", + "Asia/Kuala_Lumpur\n", + "Asia/Kuching\n", + "Asia/Kuwait\n", + "Asia/Macao\n", + "Asia/Macau\n", + "Asia/Magadan\n", + "Asia/Makassar\n", + "Asia/Manila\n", + "Asia/Muscat\n", + "Asia/Nicosia\n", + "Asia/Novokuznetsk\n", + "Asia/Novosibirsk\n", + "Asia/Omsk\n", + "Asia/Oral\n", + "Asia/Phnom_Penh\n", + "Asia/Pontianak\n", + "Asia/Pyongyang\n", + "Asia/Qatar\n", + "Asia/Qostanay\n", + "Asia/Qyzylorda\n", + "Asia/Rangoon\n", + "Asia/Riyadh\n", + "Asia/Saigon\n", + "Asia/Sakhalin\n", + "Asia/Samarkand\n", + "Asia/Seoul\n", + "Asia/Shanghai\n", + "Asia/Singapore\n", + "Asia/Srednekolymsk\n", + "Asia/Taipei\n", + "Asia/Tashkent\n", + "Asia/Tbilisi\n", + "Asia/Tehran\n", + "Asia/Tel_Aviv\n", + "Asia/Thimbu\n", + "Asia/Thimphu\n", + "Asia/Tokyo\n", + "Asia/Tomsk\n", + "Asia/Ujung_Pandang\n", + "Asia/Ulaanbaatar\n", + "Asia/Ulan_Bator\n", + "Asia/Urumqi\n", + "Asia/Ust-Nera\n", + "Asia/Vientiane\n", + "Asia/Vladivostok\n", + "Asia/Yakutsk\n", + "Asia/Yangon\n", + "Asia/Yekaterinburg\n", + "Asia/Yerevan\n", + "Atlantic/Azores\n", + "Atlantic/Bermuda\n", + "Atlantic/Canary\n", + "Atlantic/Cape_Verde\n", + "Atlantic/Faeroe\n", + "Atlantic/Faroe\n", + "Atlantic/Jan_Mayen\n", + "Atlantic/Madeira\n", + "Atlantic/Reykjavik\n", + "Atlantic/South_Georgia\n", + "Atlantic/St_Helena\n", + "Atlantic/Stanley\n", + "Australia/ACT\n", + "Australia/Adelaide\n", + "Australia/Brisbane\n", + "Australia/Broken_Hill\n", + "Australia/Canberra\n", + "Australia/Currie\n", + "Australia/Darwin\n", + "Australia/Eucla\n", + "Australia/Hobart\n", + "Australia/LHI\n", + "Australia/Lindeman\n", + "Australia/Lord_Howe\n", + "Australia/Melbourne\n", + "Australia/NSW\n", + "Australia/North\n", + "Australia/Perth\n", + "Australia/Queensland\n", + "Australia/South\n", + "Australia/Sydney\n", + "Australia/Tasmania\n", + "Australia/Victoria\n", + "Australia/West\n", + "Australia/Yancowinna\n", + "Brazil/Acre\n", + "Brazil/DeNoronha\n", + "Brazil/East\n", + "Brazil/West\n", + "CET\n", + "CST6CDT\n", + "Canada/Atlantic\n", + "Canada/Central\n", + "Canada/Eastern\n", + "Canada/Mountain\n", + "Canada/Newfoundland\n", + "Canada/Pacific\n", + "Canada/Saskatchewan\n", + "Canada/Yukon\n", + "Chile/Continental\n", + "Chile/EasterIsland\n", + "Cuba\n", + "EET\n", + "EST\n", + "EST5EDT\n", + "Egypt\n", + "Eire\n", + "Etc/GMT\n", + "Etc/GMT+0\n", + "Etc/GMT+1\n", + "Etc/GMT+10\n", + "Etc/GMT+11\n", + "Etc/GMT+12\n", + "Etc/GMT+2\n", + "Etc/GMT+3\n", + "Etc/GMT+4\n", + "Etc/GMT+5\n", + "Etc/GMT+6\n", + "Etc/GMT+7\n", + "Etc/GMT+8\n", + "Etc/GMT+9\n", + "Etc/GMT-0\n", + "Etc/GMT-1\n", + "Etc/GMT-10\n", + "Etc/GMT-11\n", + "Etc/GMT-12\n", + "Etc/GMT-13\n", + "Etc/GMT-14\n", + "Etc/GMT-2\n", + "Etc/GMT-3\n", + "Etc/GMT-4\n", + "Etc/GMT-5\n", + "Etc/GMT-6\n", + "Etc/GMT-7\n", + "Etc/GMT-8\n", + "Etc/GMT-9\n", + "Etc/GMT0\n", + "Etc/Greenwich\n", + "Etc/UCT\n", + "Etc/UTC\n", + "Etc/Universal\n", + "Etc/Zulu\n", + "Europe/Amsterdam\n", + "Europe/Andorra\n", + "Europe/Astrakhan\n", + "Europe/Athens\n", + "Europe/Belfast\n", + "Europe/Belgrade\n", + "Europe/Berlin\n", + "Europe/Bratislava\n", + "Europe/Brussels\n", + "Europe/Bucharest\n", + "Europe/Budapest\n", + "Europe/Busingen\n", + "Europe/Chisinau\n", + "Europe/Copenhagen\n", + "Europe/Dublin\n", + "Europe/Gibraltar\n", + "Europe/Guernsey\n", + "Europe/Helsinki\n", + "Europe/Isle_of_Man\n", + "Europe/Istanbul\n", + "Europe/Jersey\n", + "Europe/Kaliningrad\n", + "Europe/Kiev\n", + "Europe/Kirov\n", + "Europe/Lisbon\n", + "Europe/Ljubljana\n", + "Europe/London\n", + "Europe/Luxembourg\n", + "Europe/Madrid\n", + "Europe/Malta\n", + "Europe/Mariehamn\n", + "Europe/Minsk\n", + "Europe/Monaco\n", + "Europe/Moscow\n", + "Europe/Nicosia\n", + "Europe/Oslo\n", + "Europe/Paris\n", + "Europe/Podgorica\n", + "Europe/Prague\n", + "Europe/Riga\n", + "Europe/Rome\n", + "Europe/Samara\n", + "Europe/San_Marino\n", + "Europe/Sarajevo\n", + "Europe/Saratov\n", + "Europe/Simferopol\n", + "Europe/Skopje\n", + "Europe/Sofia\n", + "Europe/Stockholm\n", + "Europe/Tallinn\n", + "Europe/Tirane\n", + "Europe/Tiraspol\n", + "Europe/Ulyanovsk\n", + "Europe/Uzhgorod\n", + "Europe/Vaduz\n", + "Europe/Vatican\n", + "Europe/Vienna\n", + "Europe/Vilnius\n", + "Europe/Volgograd\n", + "Europe/Warsaw\n", + "Europe/Zagreb\n", + "Europe/Zaporozhye\n", + "Europe/Zurich\n", + "GB\n", + "GB-Eire\n", + "GMT\n", + "GMT+0\n", + "GMT-0\n", + "GMT0\n", + "Greenwich\n", + "HST\n", + "Hongkong\n", + "Iceland\n", + "Indian/Antananarivo\n", + "Indian/Chagos\n", + "Indian/Christmas\n", + "Indian/Cocos\n", + "Indian/Comoro\n", + "Indian/Kerguelen\n", + "Indian/Mahe\n", + "Indian/Maldives\n", + "Indian/Mauritius\n", + "Indian/Mayotte\n", + "Indian/Reunion\n", + "Iran\n", + "Israel\n", + "Jamaica\n", + "Japan\n", + "Kwajalein\n", + "Libya\n", + "MET\n", + "MST\n", + "MST7MDT\n", + "Mexico/BajaNorte\n", + "Mexico/BajaSur\n", + "Mexico/General\n", + "NZ\n", + "NZ-CHAT\n", + "Navajo\n", + "PRC\n", + "PST8PDT\n", + "Pacific/Apia\n", + "Pacific/Auckland\n", + "Pacific/Bougainville\n", + "Pacific/Chatham\n", + "Pacific/Chuuk\n", + "Pacific/Easter\n", + "Pacific/Efate\n", + "Pacific/Enderbury\n", + "Pacific/Fakaofo\n", + "Pacific/Fiji\n", + "Pacific/Funafuti\n", + "Pacific/Galapagos\n", + "Pacific/Gambier\n", + "Pacific/Guadalcanal\n", + "Pacific/Guam\n", + "Pacific/Honolulu\n", + "Pacific/Johnston\n", + "Pacific/Kanton\n", + "Pacific/Kiritimati\n", + "Pacific/Kosrae\n", + "Pacific/Kwajalein\n", + "Pacific/Majuro\n", + "Pacific/Marquesas\n", + "Pacific/Midway\n", + "Pacific/Nauru\n", + "Pacific/Niue\n", + "Pacific/Norfolk\n", + "Pacific/Noumea\n", + "Pacific/Pago_Pago\n", + "Pacific/Palau\n", + "Pacific/Pitcairn\n", + "Pacific/Pohnpei\n", + "Pacific/Ponape\n", + "Pacific/Port_Moresby\n", + "Pacific/Rarotonga\n", + "Pacific/Saipan\n", + "Pacific/Samoa\n", + "Pacific/Tahiti\n", + "Pacific/Tarawa\n", + "Pacific/Tongatapu\n", + "Pacific/Truk\n", + "Pacific/Wake\n", + "Pacific/Wallis\n", + "Pacific/Yap\n", + "Poland\n", + "Portugal\n", + "ROC\n", + "ROK\n", + "Singapore\n", + "Turkey\n", + "UCT\n", + "US/Alaska\n", + "US/Aleutian\n", + "US/Arizona\n", + "US/Central\n", + "US/East-Indiana\n", + "US/Eastern\n", + "US/Hawaii\n", + "US/Indiana-Starke\n", + "US/Michigan\n", + "US/Mountain\n", + "US/Pacific\n", + "US/Samoa\n", + "UTC\n", + "Universal\n", + "W-SU\n", + "WET\n", + "Zulu\n" + ] + } + ], + "source": [ + "for tz in pytz.all_timezones:\n", + " print(tz)" + ] + }, + { + "cell_type": "markdown", + "id": "f18fa2c9", + "metadata": {}, + "source": [ + "With the `zoneinfo` module:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "9d2cf9d1", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Africa/Abidjan\n", + "Africa/Accra\n", + "Africa/Addis_Ababa\n", + "Africa/Algiers\n", + "Africa/Asmara\n", + "Africa/Asmera\n", + "Africa/Bamako\n", + "Africa/Bangui\n", + "Africa/Banjul\n", + "Africa/Bissau\n", + "Africa/Blantyre\n", + "Africa/Brazzaville\n", + "Africa/Bujumbura\n", + "Africa/Cairo\n", + "Africa/Casablanca\n", + "Africa/Ceuta\n", + "Africa/Conakry\n", + "Africa/Dakar\n", + "Africa/Dar_es_Salaam\n", + "Africa/Djibouti\n", + "Africa/Douala\n", + "Africa/El_Aaiun\n", + "Africa/Freetown\n", + "Africa/Gaborone\n", + "Africa/Harare\n", + "Africa/Johannesburg\n", + "Africa/Juba\n", + "Africa/Kampala\n", + "Africa/Khartoum\n", + "Africa/Kigali\n", + "Africa/Kinshasa\n", + "Africa/Lagos\n", + "Africa/Libreville\n", + "Africa/Lome\n", + "Africa/Luanda\n", + "Africa/Lubumbashi\n", + "Africa/Lusaka\n", + "Africa/Malabo\n", + "Africa/Maputo\n", + "Africa/Maseru\n", + "Africa/Mbabane\n", + "Africa/Mogadishu\n", + "Africa/Monrovia\n", + "Africa/Nairobi\n", + "Africa/Ndjamena\n", + "Africa/Niamey\n", + "Africa/Nouakchott\n", + "Africa/Ouagadougou\n", + "Africa/Porto-Novo\n", + "Africa/Sao_Tome\n", + "Africa/Timbuktu\n", + "Africa/Tripoli\n", + "Africa/Tunis\n", + "Africa/Windhoek\n", + "America/Adak\n", + "America/Anchorage\n", + "America/Anguilla\n", + "America/Antigua\n", + "America/Araguaina\n", + "America/Argentina/Buenos_Aires\n", + "America/Argentina/Catamarca\n", + "America/Argentina/ComodRivadavia\n", + "America/Argentina/Cordoba\n", + "America/Argentina/Jujuy\n", + "America/Argentina/La_Rioja\n", + "America/Argentina/Mendoza\n", + "America/Argentina/Rio_Gallegos\n", + "America/Argentina/Salta\n", + "America/Argentina/San_Juan\n", + "America/Argentina/San_Luis\n", + "America/Argentina/Tucuman\n", + "America/Argentina/Ushuaia\n", + "America/Aruba\n", + "America/Asuncion\n", + "America/Atikokan\n", + "America/Atka\n", + "America/Bahia\n", + "America/Bahia_Banderas\n", + "America/Barbados\n", + "America/Belem\n", + "America/Belize\n", + "America/Blanc-Sablon\n", + "America/Boa_Vista\n", + "America/Bogota\n", + "America/Boise\n", + "America/Buenos_Aires\n", + "America/Cambridge_Bay\n", + "America/Campo_Grande\n", + "America/Cancun\n", + "America/Caracas\n", + "America/Catamarca\n", + "America/Cayenne\n", + "America/Cayman\n", + "America/Chicago\n", + "America/Chihuahua\n", + "America/Coral_Harbour\n", + "America/Cordoba\n", + "America/Costa_Rica\n", + "America/Creston\n", + "America/Cuiaba\n", + "America/Curacao\n", + "America/Danmarkshavn\n", + "America/Dawson\n", + "America/Dawson_Creek\n", + "America/Denver\n", + "America/Detroit\n", + "America/Dominica\n", + "America/Edmonton\n", + "America/Eirunepe\n", + "America/El_Salvador\n", + "America/Ensenada\n", + "America/Fort_Nelson\n", + "America/Fort_Wayne\n", + "America/Fortaleza\n", + "America/Glace_Bay\n", + "America/Godthab\n", + "America/Goose_Bay\n", + "America/Grand_Turk\n", + "America/Grenada\n", + "America/Guadeloupe\n", + "America/Guatemala\n", + "America/Guayaquil\n", + "America/Guyana\n", + "America/Halifax\n", + "America/Havana\n", + "America/Hermosillo\n", + "America/Indiana/Indianapolis\n", + "America/Indiana/Knox\n", + "America/Indiana/Marengo\n", + "America/Indiana/Petersburg\n", + "America/Indiana/Tell_City\n", + "America/Indiana/Vevay\n", + "America/Indiana/Vincennes\n", + "America/Indiana/Winamac\n", + "America/Indianapolis\n", + "America/Inuvik\n", + "America/Iqaluit\n", + "America/Jamaica\n", + "America/Jujuy\n", + "America/Juneau\n", + "America/Kentucky/Louisville\n", + "America/Kentucky/Monticello\n", + "America/Knox_IN\n", + "America/Kralendijk\n", + "America/La_Paz\n", + "America/Lima\n", + "America/Los_Angeles\n", + "America/Louisville\n", + "America/Lower_Princes\n", + "America/Maceio\n", + "America/Managua\n", + "America/Manaus\n", + "America/Marigot\n", + "America/Martinique\n", + "America/Matamoros\n", + "America/Mazatlan\n", + "America/Mendoza\n", + "America/Menominee\n", + "America/Merida\n", + "America/Metlakatla\n", + "America/Mexico_City\n", + "America/Miquelon\n", + "America/Moncton\n", + "America/Monterrey\n", + "America/Montevideo\n", + "America/Montreal\n", + "America/Montserrat\n", + "America/Nassau\n", + "America/New_York\n", + "America/Nipigon\n", + "America/Nome\n", + "America/Noronha\n", + "America/North_Dakota/Beulah\n", + "America/North_Dakota/Center\n", + "America/North_Dakota/New_Salem\n", + "America/Nuuk\n", + "America/Ojinaga\n", + "America/Panama\n", + "America/Pangnirtung\n", + "America/Paramaribo\n", + "America/Phoenix\n", + "America/Port-au-Prince\n", + "America/Port_of_Spain\n", + "America/Porto_Acre\n", + "America/Porto_Velho\n", + "America/Puerto_Rico\n", + "America/Punta_Arenas\n", + "America/Rainy_River\n", + "America/Rankin_Inlet\n", + "America/Recife\n", + "America/Regina\n", + "America/Resolute\n", + "America/Rio_Branco\n", + "America/Rosario\n", + "America/Santa_Isabel\n", + "America/Santarem\n", + "America/Santiago\n", + "America/Santo_Domingo\n", + "America/Sao_Paulo\n", + "America/Scoresbysund\n", + "America/Shiprock\n", + "America/Sitka\n", + "America/St_Barthelemy\n", + "America/St_Johns\n", + "America/St_Kitts\n", + "America/St_Lucia\n", + "America/St_Thomas\n", + "America/St_Vincent\n", + "America/Swift_Current\n", + "America/Tegucigalpa\n", + "America/Thule\n", + "America/Thunder_Bay\n", + "America/Tijuana\n", + "America/Toronto\n", + "America/Tortola\n", + "America/Vancouver\n", + "America/Virgin\n", + "America/Whitehorse\n", + "America/Winnipeg\n", + "America/Yakutat\n", + "America/Yellowknife\n", + "Antarctica/Casey\n", + "Antarctica/Davis\n", + "Antarctica/DumontDUrville\n", + "Antarctica/Macquarie\n", + "Antarctica/Mawson\n", + "Antarctica/McMurdo\n", + "Antarctica/Palmer\n", + "Antarctica/Rothera\n", + "Antarctica/South_Pole\n", + "Antarctica/Syowa\n", + "Antarctica/Troll\n", + "Antarctica/Vostok\n", + "Arctic/Longyearbyen\n", + "Asia/Aden\n", + "Asia/Almaty\n", + "Asia/Amman\n", + "Asia/Anadyr\n", + "Asia/Aqtau\n", + "Asia/Aqtobe\n", + "Asia/Ashgabat\n", + "Asia/Ashkhabad\n", + "Asia/Atyrau\n", + "Asia/Baghdad\n", + "Asia/Bahrain\n", + "Asia/Baku\n", + "Asia/Bangkok\n", + "Asia/Barnaul\n", + "Asia/Beirut\n", + "Asia/Bishkek\n", + "Asia/Brunei\n", + "Asia/Calcutta\n", + "Asia/Chita\n", + "Asia/Choibalsan\n", + "Asia/Chongqing\n", + "Asia/Chungking\n", + "Asia/Colombo\n", + "Asia/Dacca\n", + "Asia/Damascus\n", + "Asia/Dhaka\n", + "Asia/Dili\n", + "Asia/Dubai\n", + "Asia/Dushanbe\n", + "Asia/Famagusta\n", + "Asia/Gaza\n", + "Asia/Harbin\n", + "Asia/Hebron\n", + "Asia/Ho_Chi_Minh\n", + "Asia/Hong_Kong\n", + "Asia/Hovd\n", + "Asia/Irkutsk\n", + "Asia/Istanbul\n", + "Asia/Jakarta\n", + "Asia/Jayapura\n", + "Asia/Jerusalem\n", + "Asia/Kabul\n", + "Asia/Kamchatka\n", + "Asia/Karachi\n", + "Asia/Kashgar\n", + "Asia/Kathmandu\n", + "Asia/Katmandu\n", + "Asia/Khandyga\n", + "Asia/Kolkata\n", + "Asia/Krasnoyarsk\n", + "Asia/Kuala_Lumpur\n", + "Asia/Kuching\n", + "Asia/Kuwait\n", + "Asia/Macao\n", + "Asia/Macau\n", + "Asia/Magadan\n", + "Asia/Makassar\n", + "Asia/Manila\n", + "Asia/Muscat\n", + "Asia/Nicosia\n", + "Asia/Novokuznetsk\n", + "Asia/Novosibirsk\n", + "Asia/Omsk\n", + "Asia/Oral\n", + "Asia/Phnom_Penh\n", + "Asia/Pontianak\n", + "Asia/Pyongyang\n", + "Asia/Qatar\n", + "Asia/Qostanay\n", + "Asia/Qyzylorda\n", + "Asia/Rangoon\n", + "Asia/Riyadh\n", + "Asia/Saigon\n", + "Asia/Sakhalin\n", + "Asia/Samarkand\n", + "Asia/Seoul\n", + "Asia/Shanghai\n", + "Asia/Singapore\n", + "Asia/Srednekolymsk\n", + "Asia/Taipei\n", + "Asia/Tashkent\n", + "Asia/Tbilisi\n", + "Asia/Tehran\n", + "Asia/Tel_Aviv\n", + "Asia/Thimbu\n", + "Asia/Thimphu\n", + "Asia/Tokyo\n", + "Asia/Tomsk\n", + "Asia/Ujung_Pandang\n", + "Asia/Ulaanbaatar\n", + "Asia/Ulan_Bator\n", + "Asia/Urumqi\n", + "Asia/Ust-Nera\n", + "Asia/Vientiane\n", + "Asia/Vladivostok\n", + "Asia/Yakutsk\n", + "Asia/Yangon\n", + "Asia/Yekaterinburg\n", + "Asia/Yerevan\n", + "Atlantic/Azores\n", + "Atlantic/Bermuda\n", + "Atlantic/Canary\n", + "Atlantic/Cape_Verde\n", + "Atlantic/Faeroe\n", + "Atlantic/Faroe\n", + "Atlantic/Jan_Mayen\n", + "Atlantic/Madeira\n", + "Atlantic/Reykjavik\n", + "Atlantic/South_Georgia\n", + "Atlantic/St_Helena\n", + "Atlantic/Stanley\n", + "Australia/ACT\n", + "Australia/Adelaide\n", + "Australia/Brisbane\n", + "Australia/Broken_Hill\n", + "Australia/Canberra\n", + "Australia/Currie\n", + "Australia/Darwin\n", + "Australia/Eucla\n", + "Australia/Hobart\n", + "Australia/LHI\n", + "Australia/Lindeman\n", + "Australia/Lord_Howe\n", + "Australia/Melbourne\n", + "Australia/NSW\n", + "Australia/North\n", + "Australia/Perth\n", + "Australia/Queensland\n", + "Australia/South\n", + "Australia/Sydney\n", + "Australia/Tasmania\n", + "Australia/Victoria\n", + "Australia/West\n", + "Australia/Yancowinna\n", + "Brazil/Acre\n", + "Brazil/DeNoronha\n", + "Brazil/East\n", + "Brazil/West\n", + "CET\n", + "CST6CDT\n", + "Canada/Atlantic\n", + "Canada/Central\n", + "Canada/Eastern\n", + "Canada/Mountain\n", + "Canada/Newfoundland\n", + "Canada/Pacific\n", + "Canada/Saskatchewan\n", + "Canada/Yukon\n", + "Chile/Continental\n", + "Chile/EasterIsland\n", + "Cuba\n", + "EET\n", + "EST\n", + "EST5EDT\n", + "Egypt\n", + "Eire\n", + "Etc/GMT\n", + "Etc/GMT+0\n", + "Etc/GMT+1\n", + "Etc/GMT+10\n", + "Etc/GMT+11\n", + "Etc/GMT+12\n", + "Etc/GMT+2\n", + "Etc/GMT+3\n", + "Etc/GMT+4\n", + "Etc/GMT+5\n", + "Etc/GMT+6\n", + "Etc/GMT+7\n", + "Etc/GMT+8\n", + "Etc/GMT+9\n", + "Etc/GMT-0\n", + "Etc/GMT-1\n", + "Etc/GMT-10\n", + "Etc/GMT-11\n", + "Etc/GMT-12\n", + "Etc/GMT-13\n", + "Etc/GMT-14\n", + "Etc/GMT-2\n", + "Etc/GMT-3\n", + "Etc/GMT-4\n", + "Etc/GMT-5\n", + "Etc/GMT-6\n", + "Etc/GMT-7\n", + "Etc/GMT-8\n", + "Etc/GMT-9\n", + "Etc/GMT0\n", + "Etc/Greenwich\n", + "Etc/UCT\n", + "Etc/UTC\n", + "Etc/Universal\n", + "Etc/Zulu\n", + "Europe/Amsterdam\n", + "Europe/Andorra\n", + "Europe/Astrakhan\n", + "Europe/Athens\n", + "Europe/Belfast\n", + "Europe/Belgrade\n", + "Europe/Berlin\n", + "Europe/Bratislava\n", + "Europe/Brussels\n", + "Europe/Bucharest\n", + "Europe/Budapest\n", + "Europe/Busingen\n", + "Europe/Chisinau\n", + "Europe/Copenhagen\n", + "Europe/Dublin\n", + "Europe/Gibraltar\n", + "Europe/Guernsey\n", + "Europe/Helsinki\n", + "Europe/Isle_of_Man\n", + "Europe/Istanbul\n", + "Europe/Jersey\n", + "Europe/Kaliningrad\n", + "Europe/Kiev\n", + "Europe/Kirov\n", + "Europe/Lisbon\n", + "Europe/Ljubljana\n", + "Europe/London\n", + "Europe/Luxembourg\n", + "Europe/Madrid\n", + "Europe/Malta\n", + "Europe/Mariehamn\n", + "Europe/Minsk\n", + "Europe/Monaco\n", + "Europe/Moscow\n", + "Europe/Nicosia\n", + "Europe/Oslo\n", + "Europe/Paris\n", + "Europe/Podgorica\n", + "Europe/Prague\n", + "Europe/Riga\n", + "Europe/Rome\n", + "Europe/Samara\n", + "Europe/San_Marino\n", + "Europe/Sarajevo\n", + "Europe/Saratov\n", + "Europe/Simferopol\n", + "Europe/Skopje\n", + "Europe/Sofia\n", + "Europe/Stockholm\n", + "Europe/Tallinn\n", + "Europe/Tirane\n", + "Europe/Tiraspol\n", + "Europe/Ulyanovsk\n", + "Europe/Uzhgorod\n", + "Europe/Vaduz\n", + "Europe/Vatican\n", + "Europe/Vienna\n", + "Europe/Vilnius\n", + "Europe/Volgograd\n", + "Europe/Warsaw\n", + "Europe/Zagreb\n", + "Europe/Zaporozhye\n", + "Europe/Zurich\n", + "Factory\n", + "GB\n", + "GB-Eire\n", + "GMT\n", + "GMT+0\n", + "GMT-0\n", + "GMT0\n", + "Greenwich\n", + "HST\n", + "Hongkong\n", + "Iceland\n", + "Indian/Antananarivo\n", + "Indian/Chagos\n", + "Indian/Christmas\n", + "Indian/Cocos\n", + "Indian/Comoro\n", + "Indian/Kerguelen\n", + "Indian/Mahe\n", + "Indian/Maldives\n", + "Indian/Mauritius\n", + "Indian/Mayotte\n", + "Indian/Reunion\n", + "Iran\n", + "Israel\n", + "Jamaica\n", + "Japan\n", + "Kwajalein\n", + "Libya\n", + "MET\n", + "MST\n", + "MST7MDT\n", + "Mexico/BajaNorte\n", + "Mexico/BajaSur\n", + "Mexico/General\n", + "NZ\n", + "NZ-CHAT\n", + "Navajo\n", + "PRC\n", + "PST8PDT\n", + "Pacific/Apia\n", + "Pacific/Auckland\n", + "Pacific/Bougainville\n", + "Pacific/Chatham\n", + "Pacific/Chuuk\n", + "Pacific/Easter\n", + "Pacific/Efate\n", + "Pacific/Enderbury\n", + "Pacific/Fakaofo\n", + "Pacific/Fiji\n", + "Pacific/Funafuti\n", + "Pacific/Galapagos\n", + "Pacific/Gambier\n", + "Pacific/Guadalcanal\n", + "Pacific/Guam\n", + "Pacific/Honolulu\n", + "Pacific/Johnston\n", + "Pacific/Kanton\n", + "Pacific/Kiritimati\n", + "Pacific/Kosrae\n", + "Pacific/Kwajalein\n", + "Pacific/Majuro\n", + "Pacific/Marquesas\n", + "Pacific/Midway\n", + "Pacific/Nauru\n", + "Pacific/Niue\n", + "Pacific/Norfolk\n", + "Pacific/Noumea\n", + "Pacific/Pago_Pago\n", + "Pacific/Palau\n", + "Pacific/Pitcairn\n", + "Pacific/Pohnpei\n", + "Pacific/Ponape\n", + "Pacific/Port_Moresby\n", + "Pacific/Rarotonga\n", + "Pacific/Saipan\n", + "Pacific/Samoa\n", + "Pacific/Tahiti\n", + "Pacific/Tarawa\n", + "Pacific/Tongatapu\n", + "Pacific/Truk\n", + "Pacific/Wake\n", + "Pacific/Wallis\n", + "Pacific/Yap\n", + "Poland\n", + "Portugal\n", + "ROC\n", + "ROK\n", + "Singapore\n", + "Turkey\n", + "UCT\n", + "US/Alaska\n", + "US/Aleutian\n", + "US/Arizona\n", + "US/Central\n", + "US/East-Indiana\n", + "US/Eastern\n", + "US/Hawaii\n", + "US/Indiana-Starke\n", + "US/Michigan\n", + "US/Mountain\n", + "US/Pacific\n", + "US/Samoa\n", + "UTC\n", + "Universal\n", + "W-SU\n", + "WET\n", + "Zulu\n" + ] + } + ], + "source": [ + "for tz in sorted(zoneinfo.available_timezones()):\n", + " print(tz)" + ] + }, + { + "cell_type": "markdown", + "id": "6a1887cf", + "metadata": {}, + "source": [ + "Are the time zones defined by `pytz` and `zoneinfo` the same? Yes!" + ] + }, + { + "cell_type": "markdown", + "id": "8f8df276", + "metadata": {}, + "source": [ + "In this example, let's take our current time in UTC, and convert it to some other time zone, say `Australia/Melbourne`." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "77f1b8d9", + "metadata": {}, + "outputs": [], + "source": [ + "now_utc_naive = datetime.utcnow()" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "f8586215", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "datetime.datetime(2022, 3, 20, 6, 1, 3, 368403)" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "now_utc_naive" + ] + }, + { + "cell_type": "markdown", + "id": "a452220a", + "metadata": {}, + "source": [ + "The problem here is that we have a _naive_ datetime (i.e. one without an attached timezone)." + ] + }, + { + "cell_type": "markdown", + "id": "d1d10809", + "metadata": {}, + "source": [ + "We can make this naive datetime time zone aware by tacking on the timezone (since we know it is UTC):" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "db60736b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "datetime.datetime(2022, 3, 20, 6, 1, 3, 368403, tzinfo=datetime.timezone.utc)" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "now_utc_aware = now_utc_naive.replace(tzinfo=timezone.utc)\n", + "now_utc_aware" + ] + }, + { + "cell_type": "markdown", + "id": "8282b097", + "metadata": {}, + "source": [ + "Or, we could use the `pytz` library to do the same thing:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "0d7434f7", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "datetime.datetime(2022, 3, 20, 6, 1, 3, 377626, tzinfo=)" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "pytz.utc.localize(datetime.utcnow())" + ] + }, + { + "cell_type": "markdown", + "id": "e613c2e1", + "metadata": {}, + "source": [ + "Now that we have a time zone aware datetime, we can convert it to another timezone using `pytz`:" + ] + }, + { + "cell_type": "markdown", + "id": "94b0b5c9", + "metadata": {}, + "source": [ + "First, let's pick a time zone from `pytz`:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "833b5ff9", + "metadata": {}, + "outputs": [], + "source": [ + "tz_melbourne = pytz.timezone('Australia/Melbourne')" + ] + }, + { + "cell_type": "markdown", + "id": "ce4323c1", + "metadata": {}, + "source": [ + "And now we localize our aware datetime to this time zone:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "d23fd3c4", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "datetime.datetime(2022, 3, 20, 17, 1, 3, 368403, tzinfo=)" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "now_utc_aware.astimezone(tz_melbourne)" + ] + }, + { + "cell_type": "markdown", + "id": "260bd7ff", + "metadata": {}, + "source": [ + "We could do both these steps in a single expression:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "6559daf7", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "datetime.datetime(2022, 3, 20, 17, 1, 3, 368403, tzinfo=)" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "now_utc_aware.astimezone(pytz.timezone('Australia/Melbourne'))" + ] + }, + { + "cell_type": "markdown", + "id": "6be99718", + "metadata": {}, + "source": [ + "Now, let's do the same thing using the `zoneinfo` module." + ] + }, + { + "cell_type": "markdown", + "id": "8c9b22bb", + "metadata": {}, + "source": [ + "Let's pick the same target time zone:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "30c58db3", + "metadata": {}, + "outputs": [], + "source": [ + "tz_zi_dublin = ZoneInfo(\"Europe/Dublin\")" + ] + }, + { + "cell_type": "markdown", + "id": "0f77b5b0", + "metadata": {}, + "source": [ + "And the let's convert our aware datetime to that time zone:" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "1623322d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "datetime.datetime(2022, 3, 20, 6, 1, 3, 368403, tzinfo=zoneinfo.ZoneInfo(key='Europe/Dublin'))" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "now_utc_aware.astimezone(tz_zi_dublin)" + ] + }, + { + "cell_type": "markdown", + "id": "4536470f", + "metadata": {}, + "source": [ + "Or, we can also write this as a single expression:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "337c3955", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "datetime.datetime(2022, 3, 20, 6, 1, 3, 368403, tzinfo=zoneinfo.ZoneInfo(key='Europe/Dublin'))" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "now_utc_aware.astimezone(ZoneInfo(\"Europe/Dublin\"))" + ] + }, + { + "cell_type": "markdown", + "id": "4076195d", + "metadata": {}, + "source": [ + "#### The `math` Module" + ] + }, + { + "cell_type": "markdown", + "id": "41d86e28", + "metadata": {}, + "source": [ + "Several enhancements or additions have been to the math library." + ] + }, + { + "cell_type": "markdown", + "id": "bd3de95f", + "metadata": {}, + "source": [ + "The `math` module already had the `gcd` function to calculate the great common divisor of two numbers:" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "911df27e", + "metadata": {}, + "outputs": [], + "source": [ + "import math" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "4154629e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "9" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "math.gcd(27, 45)" + ] + }, + { + "cell_type": "markdown", + "id": "85abe1fa", + "metadata": {}, + "source": [ + "But now `gcd` can take multiple arguments, not just two:" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "d64ea0d9", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "math.gcd(27, 45, 18, 15)" + ] + }, + { + "cell_type": "markdown", + "id": "0503d78a", + "metadata": {}, + "source": [ + "The `lcm` (least common multiple) function has been added:" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "b4c25fef", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "12" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "math.lcm(2, 3, 4)" + ] + }, + { + "cell_type": "markdown", + "id": "58a4c3cd", + "metadata": {}, + "source": [ + "#### Dictionary Unions" + ] + }, + { + "cell_type": "markdown", + "id": "2c05f130", + "metadata": {}, + "source": [ + "When we discussed dictionaries in this course, we saw that we could combine two dictionaries using unpacking:" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "136c8836", + "metadata": {}, + "outputs": [], + "source": [ + "d1 = {'a': 1, 'b': 2, 'c': 3}\n", + "d2 = {'c': 30, 'd': 40}" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "81331802", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'a': 1, 'b': 2, 'c': 30, 'd': 40}" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "{**d1, **d2}" + ] + }, + { + "cell_type": "markdown", + "id": "97ca030f", + "metadata": {}, + "source": [ + "As we saw the second dictionary's key/value pair \"overwrote\" the key/value pair from the first dictionary." + ] + }, + { + "cell_type": "markdown", + "id": "b361771e", + "metadata": {}, + "source": [ + "We could also use the `ChainMap` function in the `collections` module:" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "d633f16c", + "metadata": {}, + "outputs": [], + "source": [ + "from collections import ChainMap" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "ad250dfd", + "metadata": {}, + "outputs": [], + "source": [ + "merged = ChainMap(d1, d2)" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "1deeb8f2", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(1, 3, 40)" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "merged['a'], merged['c'], merged['d']" + ] + }, + { + "cell_type": "markdown", + "id": "1751d47a", + "metadata": {}, + "source": [ + "As you can see, in the `ChainMap`, the firest occurrence of the key is used - so in this case `c` comes from `d1`, not `d2`." + ] + }, + { + "cell_type": "markdown", + "id": "1c63e893", + "metadata": {}, + "source": [ + "Both of these ways of \"combining\" dictionaries work well - but they are not very intuitive, and need a little attention to what happens when you have common keys in the dictionaries." + ] + }, + { + "cell_type": "markdown", + "id": "aca5787b", + "metadata": {}, + "source": [ + "Think of concatenating lists where we can simply use the `+` operator - this is very intuitive:" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "de850747", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[1, 2, 3, 4, 5, 6]" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "[1, 2, 3] + [4, 5, 6]" + ] + }, + { + "cell_type": "markdown", + "id": "69429bab", + "metadata": {}, + "source": [ + "Now dictionaries are not like lists, but they are closely related to **sets**. With sets, we have the **union** operator (`|`):" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "dcb8a7b5", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'a', 'b', 'c', 'd'}" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "s1 = {'a', 'b', 'c'}\n", + "s2 = {'c', 'd'}\n", + "\n", + "s1 | s2" + ] + }, + { + "cell_type": "markdown", + "id": "32c20dfb", + "metadata": {}, + "source": [ + "Python 3.9 introduces support for the **union** (`|`) operation between dictionaries as well." + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "af3fcc01", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'a': 1, 'b': 2, 'c': 30, 'd': 40}" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "d1 | d2" + ] + }, + { + "cell_type": "markdown", + "id": "851ffadc", + "metadata": {}, + "source": [ + "Just like with the `{**d1, **d2}` approach, the value for `c` came from the second dictionary." + ] + }, + { + "cell_type": "markdown", + "id": "cb6fc060", + "metadata": {}, + "source": [ + "And just like with that technique we can control this by switching the order of the dictionaries in the union:" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "05555100", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'c': 3, 'd': 40, 'a': 1, 'b': 2}" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "d2 | d1" + ] + }, + { + "cell_type": "markdown", + "id": "739e210f", + "metadata": {}, + "source": [ + "One question we should have, is what happens to the insertion order that Python dictionaries now guarantee?" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "f8759da5", + "metadata": {}, + "outputs": [], + "source": [ + "d1 = {'c': 3, 'a': 1, 'b': 2}\n", + "d2 = {'d': 40, 'c': 30}" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "e06e3966", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'c': 30, 'a': 1, 'b': 2, 'd': 40}" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "d1 | d2" + ] + }, + { + "cell_type": "markdown", + "id": "8e0e1c2e", + "metadata": {}, + "source": [ + "As you can see, even though the **value** for `c` came from the **second** dictionary, the original inertion order of the **keys** is maintained, so `c` is still in first position in the union of the two dictionaries." + ] + }, + { + "cell_type": "markdown", + "id": "e12dee6a", + "metadata": {}, + "source": [ + "#### String Methods" + ] + }, + { + "cell_type": "markdown", + "id": "f69f79e4", + "metadata": {}, + "source": [ + "Often we need to remove some prefix or suffix in a string." + ] + }, + { + "cell_type": "markdown", + "id": "67496793", + "metadata": {}, + "source": [ + "For example, we may have this list of string:" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "9da4cabe", + "metadata": {}, + "outputs": [], + "source": [ + "data = [\n", + " \"(log) [2022-03-01T13:30:01] Log record 1\",\n", + " \"(log) [2022-03-01T13:30:02] Log record 2\",\n", + " \"(log) [2022-03-01T13:30:03] Log record 3\",\n", + " \"(log) [2022-03-01T13:30:04] Log record 4\",\n", + "]" + ] + }, + { + "cell_type": "markdown", + "id": "30de1de8", + "metadata": {}, + "source": [ + "And we want to clean these up and remove the `(log) ` prefix (including the space)." + ] + }, + { + "cell_type": "markdown", + "id": "84a6c502", + "metadata": {}, + "source": [ + "We can certainly do it this way:" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "8a60ecdc", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['[2022-03-01T13:30:01] Log record 1',\n", + " '[2022-03-01T13:30:02] Log record 2',\n", + " '[2022-03-01T13:30:03] Log record 3',\n", + " '[2022-03-01T13:30:04] Log record 4']" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "clean = [\n", + " s.replace(\"(log) \", '')\n", + " for s in data\n", + "]\n", + "clean" + ] + }, + { + "cell_type": "markdown", + "id": "45fa082e", + "metadata": {}, + "source": [ + "You might be tempted to use the `lstrip` method:" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "id": "74e60a43", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['[2022-03-01T13:30:01] Log record 1',\n", + " '[2022-03-01T13:30:02] Log record 2',\n", + " '[2022-03-01T13:30:03] Log record 3',\n", + " '[2022-03-01T13:30:04] Log record 4']" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "clean = [\n", + " s.lstrip(\"(log) \")\n", + " for s in data\n", + "]\n", + "clean" + ] + }, + { + "cell_type": "markdown", + "id": "5e045ebc", + "metadata": {}, + "source": [ + "This appears to work, but `lstrip` (and `rstrip`) does not interpet `\"(log )\"` as a string, but rather a **sequence** of characters, and each one will be removed - so you might end up with this problem:" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "id": "ddc6e3a8", + "metadata": {}, + "outputs": [], + "source": [ + "data2 = [\n", + " \"(log) log: [2022-03-01T13:30:01] Log record 1\",\n", + " \"(log) log: [2022-03-01T13:30:02] Log record 2\",\n", + " \"(log) log: [2022-03-01T13:30:03] Log record 3\",\n", + " \"(log) log: [2022-03-01T13:30:04] Log record 4\",\n", + "]" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "id": "d87cd3c5", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[': [2022-03-01T13:30:01] Log record 1',\n", + " ': [2022-03-01T13:30:02] Log record 2',\n", + " ': [2022-03-01T13:30:03] Log record 3',\n", + " ': [2022-03-01T13:30:04] Log record 4']" + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "clean = [\n", + " s.lstrip(\"(log) \")\n", + " for s in data2\n", + "]\n", + "clean" + ] + }, + { + "cell_type": "markdown", + "id": "0f73aac3", + "metadata": {}, + "source": [ + "Now that removed a lot more than expected everything from those strings, unlike the replace, which will replace only the first occurrence by default:" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "id": "eaf8792b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['log: [2022-03-01T13:30:01] Log record 1',\n", + " 'log: [2022-03-01T13:30:02] Log record 2',\n", + " 'log: [2022-03-01T13:30:03] Log record 3',\n", + " 'log: [2022-03-01T13:30:04] Log record 4']" + ] + }, + "execution_count": 34, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "clean = [\n", + " s.replace(\"(log) \", '')\n", + " for s in data2\n", + "]\n", + "clean" + ] + }, + { + "cell_type": "markdown", + "id": "0aca0211", + "metadata": {}, + "source": [ + "Python 3,9 introduces two new string methods to do this without having to use `replace`, namely the `removeprefix()` and `removesuffix()` methods:" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "id": "b320eb46", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['[2022-03-01T13:30:01] Log record 1',\n", + " '[2022-03-01T13:30:02] Log record 2',\n", + " '[2022-03-01T13:30:03] Log record 3',\n", + " '[2022-03-01T13:30:04] Log record 4']" + ] + }, + "execution_count": 35, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "[\n", + " s.removeprefix(\"(log) \")\n", + " for s in data\n", + "]" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "id": "d4ebaf46", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['log: [2022-03-01T13:30:01] Log record 1',\n", + " 'log: [2022-03-01T13:30:02] Log record 2',\n", + " 'log: [2022-03-01T13:30:03] Log record 3',\n", + " 'log: [2022-03-01T13:30:04] Log record 4']" + ] + }, + "execution_count": 36, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "[\n", + " s.removeprefix(\"(log) \")\n", + " for s in data2\n", + "]" + ] + }, + { + "cell_type": "markdown", + "id": "f7a2ffd4", + "metadata": {}, + "source": [ + "Note that if the prefix (or suffix) is not found, nothing happens, the new string will be the same as the original (i.e. no exception is raised):" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "id": "829cb63b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'Python rocks!'" + ] + }, + "execution_count": 37, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "'Python rocks!'.removeprefix('Java')" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.10.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Part 3/Section 11 - Extras/03 - Relevant Python 3.10 Changes.ipynb b/Part 3/Section 11 - Extras/03 - Relevant Python 3.10 Changes.ipynb new file mode 100644 index 00000000..1e7fa9b4 --- /dev/null +++ b/Part 3/Section 11 - Extras/03 - Relevant Python 3.10 Changes.ipynb @@ -0,0 +1,991 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "c4d4fc49", + "metadata": {}, + "source": [ + "### Relevant Python 3.10 Changes" + ] + }, + { + "cell_type": "markdown", + "id": "cb1495a9", + "metadata": {}, + "source": [ + "The release of Python 3.10 has brought some new features.\n", + "\n", + "This is a summary of the ones _I_ deemed relevant to this course, and does **not** include all the changes!\n", + "\n", + "For full release details, see [here](https://docs.python.org/3/whatsnew/3.10.html)" + ] + }, + { + "cell_type": "markdown", + "id": "98c3ae0d", + "metadata": {}, + "source": [ + "Python 3.10 has improved it's error messages for syntax errors - the messages are more helpful and hopefully give you a better understanding of what may be syntactically wrong with your code." + ] + }, + { + "cell_type": "markdown", + "id": "4ae4aa37", + "metadata": {}, + "source": [ + "#### Structural Pattern Matching" + ] + }, + { + "cell_type": "markdown", + "id": "d18177a2", + "metadata": {}, + "source": [ + "One thing I often hear people ask, is, what's the Python equivalent of a `switch` statement." + ] + }, + { + "cell_type": "markdown", + "id": "36a77514", + "metadata": {}, + "source": [ + "Until now, the answer has alwasy been - there isn't one. Use `if...elif` constructs." + ] + }, + { + "cell_type": "markdown", + "id": "56fdb138", + "metadata": {}, + "source": [ + "Python 3.10 introduces a new language element (`match`) to implement something called **pattern matching**, that can be used to replicate this `switch` behavior you might be used to in other languages." + ] + }, + { + "cell_type": "markdown", + "id": "e1cf8b9d", + "metadata": {}, + "source": [ + "I'll cover some of the basics here, but you should refer to the Python [docs](https://docs.python.org/3/reference/compound_stmts.html#the-match-statement) for more information, as well as the [pep](https://peps.python.org/pep-0634/) for this feature and a [tutorial pep](https://peps.python.org/pep-0636/)." + ] + }, + { + "cell_type": "markdown", + "id": "1417630a", + "metadata": {}, + "source": [ + "Let's start with a simple `match` statement:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "bbecc554", + "metadata": {}, + "outputs": [], + "source": [ + "def respond(language):\n", + " match language:\n", + " case \"Java\":\n", + " return \"Hmm, coffee!\"\n", + " case \"Python\":\n", + " return \"I'm not scared of snakes!\"\n", + " case \"Rust\":\n", + " return \"Don't drink too much water!\"\n", + " case \"Go\":\n", + " return \"Collect $200\"\n", + " case _:\n", + " return \"I'm sorry...\"" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "d5669e92", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"I'm not scared of snakes!\"" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "respond(\"Python\")" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "90cecc86", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'Collect $200'" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "respond(\"Go\")" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "f9feb023", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"I'm sorry...\"" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "respond(\"COBOL\")" + ] + }, + { + "cell_type": "markdown", + "id": "ee7c21fb", + "metadata": {}, + "source": [ + "Here we were able to define a \"default\" match pattern by using the underscore (`_`) as our pattern - this `_` is called a **wildcard**." + ] + }, + { + "cell_type": "markdown", + "id": "b76575de", + "metadata": {}, + "source": [ + "So this is very much like the \"plain\" switch statement found in some other languages." + ] + }, + { + "cell_type": "markdown", + "id": "6e55dd9f", + "metadata": {}, + "source": [ + "But, this is where things get ineteresting, pattern matching can do much more than the simple example we just saw." + ] + }, + { + "cell_type": "markdown", + "id": "f8d495f2", + "metadata": {}, + "source": [ + "For example, you can have multiple pattern matching:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "6d5fcc0c", + "metadata": {}, + "outputs": [], + "source": [ + "def respond(language):\n", + " match language:\n", + " case \"Java\" | \"Javascript\":\n", + " return \"Love those braces!\"\n", + " case \"Python\":\n", + " return \"I'm a lumberjack and I don't need no braces\"\n", + " case _:\n", + " return \"I have no clue!\"" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "02cf21af", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'Love those braces!'" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "respond(\"Java\")" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "853368f3", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'Love those braces!'" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "respond(\"Javascript\")" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "066f985e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"I'm a lumberjack and I don't need no braces\"" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "respond(\"Python\")" + ] + }, + { + "cell_type": "markdown", + "id": "7fc43e7f", + "metadata": {}, + "source": [ + "We could match against one or more literals by using the OR pattern (`|`)" + ] + }, + { + "cell_type": "markdown", + "id": "36064a9c", + "metadata": {}, + "source": [ + "Let's look at one more example, this time matching **multiple values**." + ] + }, + { + "cell_type": "markdown", + "id": "31978910", + "metadata": {}, + "source": [ + "Suppose we have some kind of command language for driving a remote controlled robot in a maze, picking up and dropping items as it moves around. Our robot is very simple, it can move in only a few directions, and one step at a time. So to move forward three spaces, we would issue three `move forward` commands.\n", + "\n", + "Additional commands are `move backward`, `move left`, `move right`. We also have a few other commands our robot understands: `pick` and `drop` for picking up and dropping objects it might find." + ] + }, + { + "cell_type": "markdown", + "id": "63944ea5", + "metadata": {}, + "source": [ + "We might write a command interpreter this way:" + ] + }, + { + "cell_type": "markdown", + "id": "f9077f13", + "metadata": {}, + "source": [ + "Let's start by using some symbols to represent the robot's actions:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "2dde5d86", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'F': '→', 'B': '←', 'L': '↑', 'R': '↓', 'pick': '⤣', 'drop': '⤥'}" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "symbols = {\n", + " \"F\": \"\\u2192\", \n", + " \"B\": \"\\u2190\", \n", + " \"L\": \"\\u2191\", \n", + " \"R\": \"\\u2193\", \n", + " \"pick\": \"\\u2923\", \n", + " \"drop\": \"\\u2925\"\n", + "}\n", + "\n", + "symbols" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "faab4c09", + "metadata": {}, + "outputs": [], + "source": [ + "def op(command):\n", + " match command:\n", + " case \"move F\":\n", + " return symbols[\"F\"]\n", + " case \"move B\":\n", + " return symbols[\"B\"]\n", + " case \"move L\":\n", + " return symbols[\"L\"]\n", + " case \"move R\":\n", + " return symbols[\"R\"]\n", + " case \"pick\":\n", + " return symbols[\"pick\"]\n", + " case \"drop\":\n", + " return symbols[\"drop\"]\n", + " case _:\n", + " raise ValueError(f\"{command} does not compute!\")" + ] + }, + { + "cell_type": "markdown", + "id": "24555fab", + "metadata": {}, + "source": [ + "Then we could issue commands such as:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "b673a044", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'↑'" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "op(\"move L\")" + ] + }, + { + "cell_type": "markdown", + "id": "b411438f", + "metadata": {}, + "source": [ + "Or multiple sequences by maybe using a list of such commands, effectively creating a sequential program for our robot:" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "cf94014f", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['→', '→', '↑', '⤣', '↓', '↑', '→', '⤥']" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "[\n", + " op(\"move F\"),\n", + " op(\"move F\"),\n", + " op(\"move L\"),\n", + " op(\"pick\"),\n", + " op(\"move R\"),\n", + " op(\"move L\"),\n", + " op(\"move F\"),\n", + " op(\"drop\"),\n", + "]" + ] + }, + { + "cell_type": "markdown", + "id": "c520053b", + "metadata": {}, + "source": [ + "We could use something called **capturing** matched sub-patterns to simply our code somewhat:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "e7da20cf", + "metadata": {}, + "outputs": [], + "source": [ + "def op(command):\n", + " match command:\n", + " case [\"move\", (\"F\" | \"B\" | \"L\" |\"R\") as direction]:\n", + " return symbols[direction]\n", + " case \"pick\":\n", + " return symbols[\"pick\"]\n", + " case \"drop\":\n", + " return symvols[\"drop\"]\n", + " case _:\n", + " raise ValueError(f\"{command} does not compute!\")" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "c2a1c617", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'↑'" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "op([\"move\", \"L\"])" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "4edeb3dc", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'⤣'" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "op(\"pick\")" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "b13504aa", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "fly does not compute!\n" + ] + } + ], + "source": [ + "try:\n", + " op(\"fly\")\n", + "except ValueError as ex:\n", + " print(ex)" + ] + }, + { + "cell_type": "markdown", + "id": "6769c228", + "metadata": {}, + "source": [ + "This is kind of tedious, it would be nicer to write commands such as `move F F L` and `move R L F` instead." + ] + }, + { + "cell_type": "markdown", + "id": "055d2c60", + "metadata": {}, + "source": [ + "There are many ways we could solve this, but pattern matching on multiple values can be really useful here." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "372bdb17", + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "def op(command):\n", + " match command:\n", + " case ['move', *directions]:\n", + " return tuple(symbols[direction] for direction in directions)\n", + " case \"pick\":\n", + " return symbols[\"pick\"]\n", + " case \"drop\":\n", + " return symbols[\"drop\"]\n", + " case _:\n", + " raise ValueError(f\"{command} does not compute!\")" + ] + }, + { + "cell_type": "markdown", + "id": "c77bec44", + "metadata": {}, + "source": [ + "What happens here is that the pattern matcher will recognize the first word `move` and then interpret the remaining words collection them in the `directions` variable (so this syntax is very similar to unpacking)." + ] + }, + { + "cell_type": "markdown", + "id": "4ef78058", + "metadata": {}, + "source": [ + "We can now rewrite our program this way:" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "88c0c024", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[('→', '→', '↑'), '⤣', ('↓', '↑', '→'), '⤥']" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "[\n", + " op([\"move\", \"F\", \"F\", \"L\"]),\n", + " op(\"pick\"),\n", + " op([\"move\", \"R\", \"L\", \"F\"]),\n", + " op(\"drop\"),\n", + "]" + ] + }, + { + "cell_type": "markdown", + "id": "1ddc0f2e", + "metadata": {}, + "source": [ + "But now we have a slight problem:" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "ed30a762", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " 'up'\n" + ] + } + ], + "source": [ + "try:\n", + " op([\"move\", \"up\"])\n", + "except Exception as ex:\n", + " print(type(ex), ex)" + ] + }, + { + "cell_type": "markdown", + "id": "f6bbbd53", + "metadata": {}, + "source": [ + "We would rather just get our custom `ValueError`. To do this we can place a **guard** on our `case` for the `move` command, that will not only do the match but also test an additional condition:" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "8d441175", + "metadata": {}, + "outputs": [], + "source": [ + "def op(command):\n", + " match command:\n", + " case ['move', *directions] if set(directions) < symbols.keys():\n", + " return tuple(symbols[direction] for direction in directions)\n", + " case \"pick\":\n", + " return symbols[\"pick\"]\n", + " case \"drop\":\n", + " return symbols[\"drop\"]\n", + " case _:\n", + " raise ValueError(f\"{command} does not compute!\")" + ] + }, + { + "cell_type": "markdown", + "id": "441a53f6", + "metadata": {}, + "source": [ + "That `if ` statement (the **guard**) will only let the case block execute if the match is true **and** that `if` expression evaludates to `True`:" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "708c5d16", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " ['move', 'up'] does not compute!\n" + ] + } + ], + "source": [ + "try:\n", + " op([\"move\", \"up\"])\n", + "except Exception as ex:\n", + " print(type(ex), ex)" + ] + }, + { + "cell_type": "markdown", + "id": "1ecf5f83", + "metadata": {}, + "source": [ + "There are many other ways we could have done this - probably better than this, but this was to illustrate how the multiple value matching can work!" + ] + }, + { + "cell_type": "markdown", + "id": "84bec678", + "metadata": {}, + "source": [ + "I urge you to read at least this [tutorial (pep 636)](https://peps.python.org/pep-0636/) on pattern matching." + ] + }, + { + "cell_type": "markdown", + "id": "5b6a7b09", + "metadata": {}, + "source": [ + "#### The `zip` Function" + ] + }, + { + "cell_type": "markdown", + "id": "1294e5f6", + "metadata": {}, + "source": [ + "We use the built-in `zip` function all the time. As we know, the `zip` will stop iterating after the first of the iterables provided in the arguments is exhausted:" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "e33aa60d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[('a', 10), ('b', 20), ('c', 30)]" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "l1 = ['a', 'b', 'c']\n", + "l2 = [10, 20, 30, 40]\n", + "\n", + "list(zip(l1, l2))" + ] + }, + { + "cell_type": "markdown", + "id": "7d310af8", + "metadata": {}, + "source": [ + "As you can see the last element of `l2` is not included in the result since `l1` only had three elements." + ] + }, + { + "cell_type": "markdown", + "id": "a9cf9570", + "metadata": {}, + "source": [ + "We can also use the `zip_longest` function in the `itertools` module to iterate over the longest iterable, providing some default value for any other iterable that has been exhausted:" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "aefa3055", + "metadata": {}, + "outputs": [], + "source": [ + "from itertools import zip_longest" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "ce4779c9", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[('a', 10), ('b', 20), ('c', 30), ('???', 40)]" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list(zip_longest(l1, l2, fillvalue='???'))" + ] + }, + { + "cell_type": "markdown", + "id": "0eb2f702", + "metadata": {}, + "source": [ + "But what if we want to only zip iterables that have the **same** length? We would need to test the length of each iterable first - but if those were iterators instead of iterables, we will have exhausted the iterator, and `zip` would come back \"empty\":" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "3b704410", + "metadata": {}, + "outputs": [], + "source": [ + "l1 = (i ** 2 for i in range(4))\n", + "l2 = (i ** 3 for i in range(3))" + ] + }, + { + "cell_type": "markdown", + "id": "da0d866f", + "metadata": {}, + "source": [ + "We could test to see if `l1` and `l2` are the same length:" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "bc7d34c9", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "len(list(l1)) == len(list(l2))" + ] + }, + { + "cell_type": "markdown", + "id": "0a4a1974", + "metadata": {}, + "source": [ + "But, if we now try to `zip` them:" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "1c3928b9", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[]" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list(zip(l1, l2))" + ] + }, + { + "cell_type": "markdown", + "id": "9ee310e9", + "metadata": {}, + "source": [ + "In Python 3.10, the `zip` function now has a keyword-only parameter called `strict` that will just do the zip, but throw an exception if one of the arguments get exhausted before the others:" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "54722489", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "zip() argument 2 is shorter than argument 1\n" + ] + } + ], + "source": [ + "l1 = (i ** 2 for i in range(4))\n", + "l2 = (i ** 3 for i in range(3))\n", + "\n", + "try:\n", + " list(zip(l1, l2, strict=True))\n", + "except ValueError as ex:\n", + " print(ex)" + ] + }, + { + "cell_type": "markdown", + "id": "2635211a", + "metadata": {}, + "source": [ + "And works just fine if the arguments all have the same length:" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "7fb672cb", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[(0, 0, 0), (1, 1, 1), (4, 8, 16), (9, 27, 81)]" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "l1 = (i ** 2 for i in range(4))\n", + "l2 = (i ** 3 for i in range(4))\n", + "l3 = (i ** 4 for i in range(4))\n", + "\n", + "list(zip(l1, l2, l3))" + ] + }, + { + "cell_type": "markdown", + "id": "501fec3b", + "metadata": {}, + "source": [ + "So why is this useful?" + ] + }, + { + "cell_type": "markdown", + "id": "dab4260e", + "metadata": {}, + "source": [ + "In **many** cases, our code zips iterables that we expect to be of the same length. To avoid bugs in our program, we should check that this condition is true, otherwise zip will silently just zip based on the shortest one. But as we saw with iterators, that can be difficult to do without exhausting the very iterators we are trying to zip. (it can be done, it's just more code)." + ] + }, + { + "cell_type": "markdown", + "id": "c73e5ad1", + "metadata": {}, + "source": [ + "So, if you are one of the lucky devs that gets to write Python 3.10 (or higher :-) ) code, you can just use `strict` whenever you zip things together and expect that they are all of the same length. Much easier to do it this way (and, as we discuss in Exception handling, falls into the category of \"ask forgiveness later\" which we saw was the preferred way (in general) to handle exceptions in our apps, as opposed to the \"look before you leap\" approach we would have to use to test the argument lengths." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "400a56c1", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.10.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Part 3/Section 11 - Extras/01 - MappingProxyType.ipynb b/Part 3/Section 11 - Extras/04 - MappingProxyType.ipynb similarity index 100% rename from Part 3/Section 11 - Extras/01 - MappingProxyType.ipynb rename to Part 3/Section 11 - Extras/04 - MappingProxyType.ipynb diff --git a/Part 4/Section 15 - Extras/01 - Relevant Python 3.8 Changes.ipynb b/Part 4/Section 15 - Extras/01 - Relevant Python 3.8 Changes.ipynb new file mode 100644 index 00000000..699f8626 --- /dev/null +++ b/Part 4/Section 15 - Extras/01 - Relevant Python 3.8 Changes.ipynb @@ -0,0 +1,1476 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "c314393b", + "metadata": {}, + "source": [ + "### Relevant Python 3.8 Changes" + ] + }, + { + "cell_type": "markdown", + "id": "cbe45497", + "metadata": {}, + "source": [ + "The release of Python 3.8 has brought some new features.\n", + "\n", + "This is a summary of the ones _I_ deemed relevant to this course, and does **not** include all the changes!\n", + "\n", + "For full release details, see [here](https://docs.python.org/3/whatsnew/3.8.html)" + ] + }, + { + "cell_type": "markdown", + "id": "7089277d", + "metadata": {}, + "source": [ + "#### Positional Only Parameters" + ] + }, + { + "cell_type": "markdown", + "id": "61154c87", + "metadata": {}, + "source": [ + "It is now possible to define **positional-only** parameters for Python functions." + ] + }, + { + "cell_type": "markdown", + "id": "63037bda", + "metadata": {}, + "source": [ + "As we saw earlier in this course, when you define positional parameters in a function:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "13c1ba59", + "metadata": {}, + "outputs": [], + "source": [ + "def my_func(a, b):\n", + " return a + b" + ] + }, + { + "cell_type": "markdown", + "id": "a4928079", + "metadata": {}, + "source": [ + "the user is free to pass arguments either positionally:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "0bb6fea5", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "my_func(1, 2)" + ] + }, + { + "cell_type": "markdown", + "id": "be1cfa90", + "metadata": {}, + "source": [ + "or, as named arguments:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "cb04e54f", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "my_func(b=2, a=1)" + ] + }, + { + "cell_type": "markdown", + "id": "617077dd", + "metadata": {}, + "source": [ + "Some functions in Python's built-ins ared defined in such a way that certain parameters can **only** be passed positionally, for example the `print` function:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "77c6fa8a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Help on built-in function print in module builtins:\n", + "\n", + "print(...)\n", + " print(value, ..., sep=' ', end='\\n', file=sys.stdout, flush=False)\n", + " \n", + " Prints the values to a stream, or to sys.stdout by default.\n", + " Optional keyword arguments:\n", + " file: a file-like object (stream); defaults to the current sys.stdout.\n", + " sep: string inserted between values, default a space.\n", + " end: string appended after the last value, default a newline.\n", + " flush: whether to forcibly flush the stream.\n", + "\n" + ] + } + ], + "source": [ + "help(print)" + ] + }, + { + "cell_type": "markdown", + "id": "248c2581", + "metadata": {}, + "source": [ + "That `value` cannot be passed by name:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "529c5dac", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "'value' is an invalid keyword argument for print()\n" + ] + } + ], + "source": [ + "try:\n", + " print(value=\"hello\")\n", + "except TypeError as ex:\n", + " print(ex)" + ] + }, + { + "cell_type": "markdown", + "id": "f4baed67", + "metadata": {}, + "source": [ + "Instead, the parameter **must** be passed positionally:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "8c147557", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "hello\n" + ] + } + ], + "source": [ + "print(\"hello\")" + ] + }, + { + "cell_type": "markdown", + "id": "d2840374", + "metadata": {}, + "source": [ + "Until Python 3.8, it was not possible to reproduce such behavior with user-defined functions." + ] + }, + { + "cell_type": "markdown", + "id": "158674a9", + "metadata": {}, + "source": [ + "Now you can, by using the slash character(`/`). Parameters defined **before** the `/` become **positional-only** parameters:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "aeebd0f8", + "metadata": {}, + "outputs": [], + "source": [ + "def my_func(a, b, /):\n", + " return a + b" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "858727b6", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "my_func(1, 2)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "4f73c741", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "my_func() got some positional-only arguments passed as keyword arguments: 'a, b'\n" + ] + } + ], + "source": [ + "try:\n", + " my_func(a=1, b=2)\n", + "except TypeError as ex:\n", + " print(ex)" + ] + }, + { + "cell_type": "markdown", + "id": "f5967700", + "metadata": {}, + "source": [ + "You can of course mix this along with the special parameters `*` and `**`:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "d17254b3", + "metadata": {}, + "outputs": [], + "source": [ + "def my_func(a, b, /, *, c):\n", + " print(a + b + c)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "b35d884e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "13\n" + ] + } + ], + "source": [ + "my_func(1, 2, c=10)" + ] + }, + { + "cell_type": "markdown", + "id": "573aca30", + "metadata": {}, + "source": [ + "#### f-string Enhancements" + ] + }, + { + "cell_type": "markdown", + "id": "89ee51f9", + "metadata": {}, + "source": [ + "Often we use f-strings to interpolate the name of a variable and it's value:" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "f79a630b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "a=hello, b=world\n" + ] + } + ], + "source": [ + "a, b = \"hello\", \"world\"\n", + "print(f\"a={a}, b={b}\")" + ] + }, + { + "cell_type": "markdown", + "id": "24747607", + "metadata": {}, + "source": [ + "Python 3.8 provides a shortcut way of doing the same thing:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "3ae5c0c4", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "a='hello', b='world'\n" + ] + } + ], + "source": [ + "print(f\"{a=}, {b=}\")" + ] + }, + { + "cell_type": "markdown", + "id": "141558b4", + "metadata": {}, + "source": [ + "You can even use [format specifiers](https://docs.python.org/3/library/string.html#formatspec)\n", + "to further customize the output:" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "e75716ac", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "a=hello, b=world\n" + ] + } + ], + "source": [ + "print(f\"{a=:s}, {b=:s}\")" + ] + }, + { + "cell_type": "markdown", + "id": "4dcdc5d0", + "metadata": {}, + "source": [ + "Or when dealing with other types:" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "362bf6b5", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import datetime\n", + "from math import pi" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "c91b7536", + "metadata": {}, + "outputs": [], + "source": [ + "d = datetime.utcnow()\n", + "e = pi" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "545c77a2", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "d=datetime.datetime(2022, 3, 20, 6, 1, 13, 990493), e=3.141592653589793\n" + ] + } + ], + "source": [ + "print(f\"{d=}, {e=}\")" + ] + }, + { + "cell_type": "markdown", + "id": "4637ecd5", + "metadata": {}, + "source": [ + "And applying some format specifiers:" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "75c4052b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "d=2022-03-20 06:01:13, e=3.142\n" + ] + } + ], + "source": [ + "print(f\"{d=:%Y-%m-%d %H:%M:%S}, {e=:.3f}\")" + ] + }, + { + "cell_type": "markdown", + "id": "076773ed", + "metadata": {}, + "source": [ + "It will even display the text of an expression if you use one in your f-string:" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "0ce46ace", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1 + 2=3, ' '.join(sentence)='Python rocks!'\n" + ] + } + ], + "source": [ + "sentence = [\"Python\", \"rocks!\"]\n", + "print(f\"{1 + 2=}, {' '.join(sentence)=}\")" + ] + }, + { + "cell_type": "markdown", + "id": "9e3783ea", + "metadata": {}, + "source": [ + "#### The `as_integer_ratio()` Method" + ] + }, + { + "cell_type": "markdown", + "id": "15ccd5ad", + "metadata": {}, + "source": [ + "The types `bool`, `int` and `Fraction` now all implement an `as_integer_ratio()` method which returns a tuple consisting of the numerator and denominator. Remember that `Decimal` and `float` already implement the same method." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "8313fc80", + "metadata": {}, + "outputs": [], + "source": [ + "from fractions import Fraction" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "7a1ae84a", + "metadata": {}, + "outputs": [], + "source": [ + "f = Fraction(2, 3)" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "a94fc3ed", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(2, 3)" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "f.as_integer_ratio()" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "f32942ec", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(12, 1)" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a = 12\n", + "a.as_integer_ratio()" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "06980a8c", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(1, 1)" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "flag = True\n", + "flag.as_integer_ratio()" + ] + }, + { + "cell_type": "markdown", + "id": "a3091e0e", + "metadata": {}, + "source": [ + "The advantage of this is mainly for polymorphism (or duck-typing), where you can now use `as_integer_ratio` irrespective of whether the variable is a `bool`, an `int`, a `float`, a `Decimal` or a `Fraction`." + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "8a362bf8", + "metadata": {}, + "outputs": [], + "source": [ + "from decimal import Decimal" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "ab4a78aa", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(33, 100)" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Decimal(\"0.33\").as_integer_ratio()" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "5ee29a47", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(7070651414971679, 2251799813685248)" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(3.14).as_integer_ratio()" + ] + }, + { + "cell_type": "markdown", + "id": "df1e85de", + "metadata": {}, + "source": [ + "#### The `lru_cache` decorator" + ] + }, + { + "cell_type": "markdown", + "id": "226efb47", + "metadata": {}, + "source": [ + "As we saw in this course, we can use the `lru_cache` decorator to appky an LRU cache to our functions:" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "20501526", + "metadata": {}, + "outputs": [], + "source": [ + "from functools import lru_cache" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "07d699f0", + "metadata": {}, + "outputs": [], + "source": [ + "@lru_cache(maxsize=3)\n", + "def fib(n):\n", + " if n <=2 :\n", + " return 1\n", + " else:\n", + " return fib(n-1) + fib(n-2)" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "45a0cd5a", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "354224848179261915075" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "fib(100)" + ] + }, + { + "cell_type": "markdown", + "id": "fea747d4", + "metadata": {}, + "source": [ + "If we don't specify `maxsize`, it will default to `128`:" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "id": "447be276", + "metadata": {}, + "outputs": [], + "source": [ + "@lru_cache()\n", + "def fib(n):\n", + " if n <=2 :\n", + " return 1\n", + " else:\n", + " return fib(n-1) + fib(n-2)" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "id": "53fee4fd", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "354224848179261915075" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "fib(100)" + ] + }, + { + "cell_type": "markdown", + "id": "5cf8e33a", + "metadata": {}, + "source": [ + "The change made to this decorator in Python 3.8 allows us not to use those empty parentheses:" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "id": "4185658b", + "metadata": {}, + "outputs": [], + "source": [ + "@lru_cache\n", + "def fib(n):\n", + " if n <=2 :\n", + " return 1\n", + " else:\n", + " return fib(n-1) + fib(n-2)" + ] + }, + { + "cell_type": "markdown", + "id": "76a9aed5", + "metadata": {}, + "source": [ + "#### `math` Module" + ] + }, + { + "cell_type": "markdown", + "id": "0af410c0", + "metadata": {}, + "source": [ + "Many examples I use throughout this course calculate the Euclidean distance between two points:" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "id": "b511addc", + "metadata": {}, + "outputs": [], + "source": [ + "import math" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "id": "0b66c006", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1.4142135623730951\n" + ] + } + ], + "source": [ + "a = (0, 0)\n", + "b = (1, 1)\n", + "\n", + "dist = math.sqrt((b[0] - a[1]) ** 2 + (b[1] - a[1]) ** 2)\n", + "print(dist)" + ] + }, + { + "cell_type": "markdown", + "id": "d276589d", + "metadata": {}, + "source": [ + "Now, it's much easier using the `dist()` function the `math` module:" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "id": "1f774757", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "1.4142135623730951" + ] + }, + "execution_count": 36, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "math.dist(a, b)" + ] + }, + { + "cell_type": "markdown", + "id": "13afb1f1", + "metadata": {}, + "source": [ + "#### The `namedtuple` Implementation" + ] + }, + { + "cell_type": "markdown", + "id": "a22a839e", + "metadata": {}, + "source": [ + "Actually these changes were added to Python 3.7, but since I don't have a separate lecture for Python 3.7 changes (most did not apply to this course), here it is." + ] + }, + { + "cell_type": "markdown", + "id": "1a579fbb", + "metadata": {}, + "source": [ + "The `_source` attribute was **removed**. There quite a discussion on this, and the the core dev who implemented and supported this essentially gave up trying to keep this in - it was deemed to cause too much \"overhead\". So, sadly (wearing my teacher's hat), it is gone. It is no more. It's not pining, it's just dead. :-)" + ] + }, + { + "cell_type": "markdown", + "id": "d5e3bfd0", + "metadata": {}, + "source": [ + "The method I showed you for defining defaults for named tuples still works, and could still be used, but Python 3.7 added the `defaults` parameter to the named tuple definition." + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "id": "3538a2c2", + "metadata": {}, + "outputs": [], + "source": [ + "from collections import namedtuple" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "id": "024bcc51", + "metadata": {}, + "outputs": [], + "source": [ + "NT = namedtuple(\"NT\", \"a b c\", defaults=(10, 20, 30))" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "id": "9dd8a877", + "metadata": {}, + "outputs": [], + "source": [ + "nt = NT()" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "id": "db7f47e7", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "NT(a=10, b=20, c=30)" + ] + }, + "execution_count": 40, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nt" + ] + }, + { + "cell_type": "markdown", + "id": "fb0bc811", + "metadata": {}, + "source": [ + "You don't have to specify defaults for everything, but if you do not, be aware that defaults will be applied from **right** to **left**. Which makes sense given that in Python non-defaulted parameters must be defined **before** defaulted parameters." + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "id": "8f6ad2ca", + "metadata": {}, + "outputs": [], + "source": [ + "NT = namedtuple(\"NT\", \"a b c\", defaults = (20, 30))" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "id": "bc9d5345", + "metadata": {}, + "outputs": [], + "source": [ + "nt = NT(10)" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "id": "eceeaec2", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "NT(a=10, b=20, c=30)" + ] + }, + "execution_count": 43, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nt" + ] + }, + { + "cell_type": "markdown", + "id": "64188b1c", + "metadata": {}, + "source": [ + "Note that with this way of specifying defaults you can easily define the same default for all items in the named tuple using the `*` operator:" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "id": "8b36d7c3", + "metadata": {}, + "outputs": [], + "source": [ + "NT = namedtuple(\"NT\", \"a b c d e f\", defaults=(\"xyz\",) * 6)" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "id": "1d448806", + "metadata": {}, + "outputs": [], + "source": [ + "nt = NT()" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "id": "b16758de", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "NT(a='xyz', b='xyz', c='xyz', d='xyz', e='xyz', f='xyz')" + ] + }, + "execution_count": 46, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nt" + ] + }, + { + "cell_type": "markdown", + "id": "bbcbaf53", + "metadata": {}, + "source": [ + "Just be careful if you use a **mutable** type to do this!!" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "id": "91b28470", + "metadata": {}, + "outputs": [], + "source": [ + "NT = namedtuple(\"NT\", \"a b c\", defaults = ([],) * 3)" + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "id": "cad11f5c", + "metadata": {}, + "outputs": [], + "source": [ + "nt = NT()" + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "id": "0cb7ceac", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "NT(a=[], b=[], c=[])" + ] + }, + "execution_count": 49, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nt" + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "id": "466d9520", + "metadata": {}, + "outputs": [], + "source": [ + "nt.a.append(10)" + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "id": "74826099", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[10]" + ] + }, + "execution_count": 51, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nt.a" + ] + }, + { + "cell_type": "markdown", + "id": "6d00b122", + "metadata": {}, + "source": [ + "But watch this!" + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "id": "4759e6f4", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "NT(a=[10], b=[10], c=[10])" + ] + }, + "execution_count": 52, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nt" + ] + }, + { + "cell_type": "markdown", + "id": "972c666e", + "metadata": {}, + "source": [ + "I hope you understand what happened here without me telling you!" + ] + }, + { + "cell_type": "markdown", + "id": "6590d73a", + "metadata": {}, + "source": [ + "The **same** list object was re-used 3 times in the defaults." + ] + }, + { + "cell_type": "markdown", + "id": "720e13bb", + "metadata": {}, + "source": [ + "You can easily recover your defaults using the `_field_defaults` method:" + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "id": "586498ae", + "metadata": {}, + "outputs": [], + "source": [ + "NT = namedtuple(\"NT\", \"a, b, c\", defaults=(1, 2, 3))" + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "id": "20f4012e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'a': 1, 'b': 2, 'c': 3}" + ] + }, + "execution_count": 54, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "NT._field_defaults" + ] + }, + { + "cell_type": "markdown", + "id": "70c3a717", + "metadata": {}, + "source": [ + "One change of note in Python 3.8, the `_as_dict()` method now returns a standard dictionary (key ordered in the same way as the named tuple). Prior to this version, it would return an `OrderedDict` since standard Python dictionaries did not guarantee any specific key order, but since they now do, there's no need to use the `DefaultDict` anymore." + ] + }, + { + "cell_type": "markdown", + "id": "ecb5b3a0", + "metadata": {}, + "source": [ + "#### Other Things" + ] + }, + { + "cell_type": "markdown", + "id": "9d8ec24b", + "metadata": {}, + "source": [ + "These are few other odds and ends that you might find of interest:" + ] + }, + { + "cell_type": "markdown", + "id": "3602bbab", + "metadata": {}, + "source": [ + "The built-in `reversed` function now works with dictionary views:" + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "id": "4f68e46b", + "metadata": {}, + "outputs": [], + "source": [ + "d = {'a': 1, 'b': 2}" + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "id": "83fc356d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['a', 'b']" + ] + }, + "execution_count": 56, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list(d.keys())" + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "id": "10648abb", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['b', 'a']" + ] + }, + "execution_count": 57, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list(reversed(d.keys()))" + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "id": "4a4885c3", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[2, 1]" + ] + }, + "execution_count": 58, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list(reversed(d.values()))" + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "id": "13785eb9", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[('b', 2), ('a', 1)]" + ] + }, + "execution_count": 59, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list(reversed(d.items()))" + ] + }, + { + "cell_type": "markdown", + "id": "e14d5c14", + "metadata": {}, + "source": [ + "The `continue` statement was not permitted in the `finally` clause of a loop, but is now supported." + ] + }, + { + "cell_type": "markdown", + "id": "fe507127", + "metadata": {}, + "source": [ + "Earlier in Part 1, we discussed string interning, as well as how a small selection of integers are essentially \"cached\" by Python and re-used whenever the literal is encountered. This meant we could use `is` instead of `==` in some cases, and that helped us get a clearer understanding of what's going on. **BUT**, as we also discussed, you should **not**, in practice, use `is` for comparing objects such as integers, strings, etc (usually we are more interested in whether is the same value, rather than the identical object) - the fact that this works is an implementation detail and not guaranteed to work the same way from one Python version to another.\n", + "\n", + "Although linters will usually catch those kinds of issues, not everyone uses a Python linter - so Python 3.8 will now emit a **warning** if you compare variables of certain types with literal constants!" + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "id": "e659956e", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "<>:2: SyntaxWarning: \"is\" with a literal. Did you mean \"==\"?\n", + "<>:2: SyntaxWarning: \"is\" with a literal. Did you mean \"==\"?\n", + "/var/folders/kg/20pf1n8j6pjg09bwxz6cgw0r0000gn/T/ipykernel_33116/3780910533.py:2: SyntaxWarning: \"is\" with a literal. Did you mean \"==\"?\n", + " a is 1\n" + ] + }, + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 60, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a = 1\n", + "a is 1" + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "id": "d970904d", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "<>:2: SyntaxWarning: \"is\" with a literal. Did you mean \"==\"?\n", + "<>:2: SyntaxWarning: \"is\" with a literal. Did you mean \"==\"?\n", + "/var/folders/kg/20pf1n8j6pjg09bwxz6cgw0r0000gn/T/ipykernel_33116/4272392880.py:2: SyntaxWarning: \"is\" with a literal. Did you mean \"==\"?\n", + " a is 'hello'\n" + ] + }, + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 61, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a = 'hello'\n", + "a is 'hello'" + ] + }, + { + "cell_type": "markdown", + "id": "01c01a49", + "metadata": {}, + "source": [ + "But we do not get a warning in a situation such as this:" + ] + }, + { + "cell_type": "code", + "execution_count": 62, + "id": "c0d535bc", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 62, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a = [1, 2, 3]\n", + "\n", + "a is [1, 2, 3]" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.10.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Part 4/Section 15 - Extras/02 - Relevant Python 3.9 Changes.ipynb b/Part 4/Section 15 - Extras/02 - Relevant Python 3.9 Changes.ipynb new file mode 100644 index 00000000..698dadce --- /dev/null +++ b/Part 4/Section 15 - Extras/02 - Relevant Python 3.9 Changes.ipynb @@ -0,0 +1,2343 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "c45f0998", + "metadata": {}, + "source": [ + "### Relevant Python 3.9 Changes" + ] + }, + { + "cell_type": "markdown", + "id": "2e965eba", + "metadata": {}, + "source": [ + "The release of Python 3.9 has brought some new features.\n", + "\n", + "This is a summary of the ones _I_ deemed relevant to this course, and does **not** include all the changes!\n", + "\n", + "For full release details, see [here](https://docs.python.org/3/whatsnew/3.9.html)" + ] + }, + { + "cell_type": "markdown", + "id": "39255227", + "metadata": {}, + "source": [ + "#### Time Zones" + ] + }, + { + "cell_type": "markdown", + "id": "26750adb", + "metadata": {}, + "source": [ + "We don't cover 3rd party libraries in this course, but if you've worked with Python in a production environment, you will likely have come across the dreaded timezone and Daylight Savings issues that plague datetimes!" + ] + }, + { + "cell_type": "markdown", + "id": "7cdfe8a5", + "metadata": {}, + "source": [ + "Most likely you will have resorted to using the `pytz` and `python-dateutil` libraries to help with that." + ] + }, + { + "cell_type": "markdown", + "id": "bc000883", + "metadata": {}, + "source": [ + "Now, Python 3.9 is proud to introduce the `zoneinfo` module to deal with timezones properly. About time too!" + ] + }, + { + "cell_type": "markdown", + "id": "c2d652dd", + "metadata": {}, + "source": [ + "For full info on this, refer to [PEP 615](https://peps.python.org/pep-0615/).\n", + "\n", + "And the Python [docs](https://docs.python.org/3.9/library/zoneinfo.html#module-zoneinfo).\n", + "\n", + "**Windows Users**: you will likely need to add a dependency on the `tzdata` [library](https://pypi.org/project/tzdata/) for the IANA time zone database. See [this note](https://docs.python.org/3.9/library/zoneinfo.html#data-sources)\n", + "\n", + "You should also take a look at this [presentation](https://pganssle-talks.github.io/chipy-nov-2020-zoneinfo/#/) by Paul Ganssle who wrote that module - very interesting read!" + ] + }, + { + "cell_type": "markdown", + "id": "80255944", + "metadata": {}, + "source": [ + "Let's look at how we might have handled timezone and DST using `pytz` and `dateutil`, and contrast that to how we can use the new `zoneinfo` module instead." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "ec129288", + "metadata": {}, + "outputs": [], + "source": [ + "import zoneinfo\n", + "from datetime import datetime, timezone\n", + "from zoneinfo import ZoneInfo\n", + "\n", + "import dateutil\n", + "import pytz" + ] + }, + { + "cell_type": "markdown", + "id": "beadc4e2", + "metadata": {}, + "source": [ + "Let's list out all the defined time zones:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "d0058e73", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Africa/Abidjan\n", + "Africa/Accra\n", + "Africa/Addis_Ababa\n", + "Africa/Algiers\n", + "Africa/Asmara\n", + "Africa/Asmera\n", + "Africa/Bamako\n", + "Africa/Bangui\n", + "Africa/Banjul\n", + "Africa/Bissau\n", + "Africa/Blantyre\n", + "Africa/Brazzaville\n", + "Africa/Bujumbura\n", + "Africa/Cairo\n", + "Africa/Casablanca\n", + "Africa/Ceuta\n", + "Africa/Conakry\n", + "Africa/Dakar\n", + "Africa/Dar_es_Salaam\n", + "Africa/Djibouti\n", + "Africa/Douala\n", + "Africa/El_Aaiun\n", + "Africa/Freetown\n", + "Africa/Gaborone\n", + "Africa/Harare\n", + "Africa/Johannesburg\n", + "Africa/Juba\n", + "Africa/Kampala\n", + "Africa/Khartoum\n", + "Africa/Kigali\n", + "Africa/Kinshasa\n", + "Africa/Lagos\n", + "Africa/Libreville\n", + "Africa/Lome\n", + "Africa/Luanda\n", + "Africa/Lubumbashi\n", + "Africa/Lusaka\n", + "Africa/Malabo\n", + "Africa/Maputo\n", + "Africa/Maseru\n", + "Africa/Mbabane\n", + "Africa/Mogadishu\n", + "Africa/Monrovia\n", + "Africa/Nairobi\n", + "Africa/Ndjamena\n", + "Africa/Niamey\n", + "Africa/Nouakchott\n", + "Africa/Ouagadougou\n", + "Africa/Porto-Novo\n", + "Africa/Sao_Tome\n", + "Africa/Timbuktu\n", + "Africa/Tripoli\n", + "Africa/Tunis\n", + "Africa/Windhoek\n", + "America/Adak\n", + "America/Anchorage\n", + "America/Anguilla\n", + "America/Antigua\n", + "America/Araguaina\n", + "America/Argentina/Buenos_Aires\n", + "America/Argentina/Catamarca\n", + "America/Argentina/ComodRivadavia\n", + "America/Argentina/Cordoba\n", + "America/Argentina/Jujuy\n", + "America/Argentina/La_Rioja\n", + "America/Argentina/Mendoza\n", + "America/Argentina/Rio_Gallegos\n", + "America/Argentina/Salta\n", + "America/Argentina/San_Juan\n", + "America/Argentina/San_Luis\n", + "America/Argentina/Tucuman\n", + "America/Argentina/Ushuaia\n", + "America/Aruba\n", + "America/Asuncion\n", + "America/Atikokan\n", + "America/Atka\n", + "America/Bahia\n", + "America/Bahia_Banderas\n", + "America/Barbados\n", + "America/Belem\n", + "America/Belize\n", + "America/Blanc-Sablon\n", + "America/Boa_Vista\n", + "America/Bogota\n", + "America/Boise\n", + "America/Buenos_Aires\n", + "America/Cambridge_Bay\n", + "America/Campo_Grande\n", + "America/Cancun\n", + "America/Caracas\n", + "America/Catamarca\n", + "America/Cayenne\n", + "America/Cayman\n", + "America/Chicago\n", + "America/Chihuahua\n", + "America/Coral_Harbour\n", + "America/Cordoba\n", + "America/Costa_Rica\n", + "America/Creston\n", + "America/Cuiaba\n", + "America/Curacao\n", + "America/Danmarkshavn\n", + "America/Dawson\n", + "America/Dawson_Creek\n", + "America/Denver\n", + "America/Detroit\n", + "America/Dominica\n", + "America/Edmonton\n", + "America/Eirunepe\n", + "America/El_Salvador\n", + "America/Ensenada\n", + "America/Fort_Nelson\n", + "America/Fort_Wayne\n", + "America/Fortaleza\n", + "America/Glace_Bay\n", + "America/Godthab\n", + "America/Goose_Bay\n", + "America/Grand_Turk\n", + "America/Grenada\n", + "America/Guadeloupe\n", + "America/Guatemala\n", + "America/Guayaquil\n", + "America/Guyana\n", + "America/Halifax\n", + "America/Havana\n", + "America/Hermosillo\n", + "America/Indiana/Indianapolis\n", + "America/Indiana/Knox\n", + "America/Indiana/Marengo\n", + "America/Indiana/Petersburg\n", + "America/Indiana/Tell_City\n", + "America/Indiana/Vevay\n", + "America/Indiana/Vincennes\n", + "America/Indiana/Winamac\n", + "America/Indianapolis\n", + "America/Inuvik\n", + "America/Iqaluit\n", + "America/Jamaica\n", + "America/Jujuy\n", + "America/Juneau\n", + "America/Kentucky/Louisville\n", + "America/Kentucky/Monticello\n", + "America/Knox_IN\n", + "America/Kralendijk\n", + "America/La_Paz\n", + "America/Lima\n", + "America/Los_Angeles\n", + "America/Louisville\n", + "America/Lower_Princes\n", + "America/Maceio\n", + "America/Managua\n", + "America/Manaus\n", + "America/Marigot\n", + "America/Martinique\n", + "America/Matamoros\n", + "America/Mazatlan\n", + "America/Mendoza\n", + "America/Menominee\n", + "America/Merida\n", + "America/Metlakatla\n", + "America/Mexico_City\n", + "America/Miquelon\n", + "America/Moncton\n", + "America/Monterrey\n", + "America/Montevideo\n", + "America/Montreal\n", + "America/Montserrat\n", + "America/Nassau\n", + "America/New_York\n", + "America/Nipigon\n", + "America/Nome\n", + "America/Noronha\n", + "America/North_Dakota/Beulah\n", + "America/North_Dakota/Center\n", + "America/North_Dakota/New_Salem\n", + "America/Nuuk\n", + "America/Ojinaga\n", + "America/Panama\n", + "America/Pangnirtung\n", + "America/Paramaribo\n", + "America/Phoenix\n", + "America/Port-au-Prince\n", + "America/Port_of_Spain\n", + "America/Porto_Acre\n", + "America/Porto_Velho\n", + "America/Puerto_Rico\n", + "America/Punta_Arenas\n", + "America/Rainy_River\n", + "America/Rankin_Inlet\n", + "America/Recife\n", + "America/Regina\n", + "America/Resolute\n", + "America/Rio_Branco\n", + "America/Rosario\n", + "America/Santa_Isabel\n", + "America/Santarem\n", + "America/Santiago\n", + "America/Santo_Domingo\n", + "America/Sao_Paulo\n", + "America/Scoresbysund\n", + "America/Shiprock\n", + "America/Sitka\n", + "America/St_Barthelemy\n", + "America/St_Johns\n", + "America/St_Kitts\n", + "America/St_Lucia\n", + "America/St_Thomas\n", + "America/St_Vincent\n", + "America/Swift_Current\n", + "America/Tegucigalpa\n", + "America/Thule\n", + "America/Thunder_Bay\n", + "America/Tijuana\n", + "America/Toronto\n", + "America/Tortola\n", + "America/Vancouver\n", + "America/Virgin\n", + "America/Whitehorse\n", + "America/Winnipeg\n", + "America/Yakutat\n", + "America/Yellowknife\n", + "Antarctica/Casey\n", + "Antarctica/Davis\n", + "Antarctica/DumontDUrville\n", + "Antarctica/Macquarie\n", + "Antarctica/Mawson\n", + "Antarctica/McMurdo\n", + "Antarctica/Palmer\n", + "Antarctica/Rothera\n", + "Antarctica/South_Pole\n", + "Antarctica/Syowa\n", + "Antarctica/Troll\n", + "Antarctica/Vostok\n", + "Arctic/Longyearbyen\n", + "Asia/Aden\n", + "Asia/Almaty\n", + "Asia/Amman\n", + "Asia/Anadyr\n", + "Asia/Aqtau\n", + "Asia/Aqtobe\n", + "Asia/Ashgabat\n", + "Asia/Ashkhabad\n", + "Asia/Atyrau\n", + "Asia/Baghdad\n", + "Asia/Bahrain\n", + "Asia/Baku\n", + "Asia/Bangkok\n", + "Asia/Barnaul\n", + "Asia/Beirut\n", + "Asia/Bishkek\n", + "Asia/Brunei\n", + "Asia/Calcutta\n", + "Asia/Chita\n", + "Asia/Choibalsan\n", + "Asia/Chongqing\n", + "Asia/Chungking\n", + "Asia/Colombo\n", + "Asia/Dacca\n", + "Asia/Damascus\n", + "Asia/Dhaka\n", + "Asia/Dili\n", + "Asia/Dubai\n", + "Asia/Dushanbe\n", + "Asia/Famagusta\n", + "Asia/Gaza\n", + "Asia/Harbin\n", + "Asia/Hebron\n", + "Asia/Ho_Chi_Minh\n", + "Asia/Hong_Kong\n", + "Asia/Hovd\n", + "Asia/Irkutsk\n", + "Asia/Istanbul\n", + "Asia/Jakarta\n", + "Asia/Jayapura\n", + "Asia/Jerusalem\n", + "Asia/Kabul\n", + "Asia/Kamchatka\n", + "Asia/Karachi\n", + "Asia/Kashgar\n", + "Asia/Kathmandu\n", + "Asia/Katmandu\n", + "Asia/Khandyga\n", + "Asia/Kolkata\n", + "Asia/Krasnoyarsk\n", + "Asia/Kuala_Lumpur\n", + "Asia/Kuching\n", + "Asia/Kuwait\n", + "Asia/Macao\n", + "Asia/Macau\n", + "Asia/Magadan\n", + "Asia/Makassar\n", + "Asia/Manila\n", + "Asia/Muscat\n", + "Asia/Nicosia\n", + "Asia/Novokuznetsk\n", + "Asia/Novosibirsk\n", + "Asia/Omsk\n", + "Asia/Oral\n", + "Asia/Phnom_Penh\n", + "Asia/Pontianak\n", + "Asia/Pyongyang\n", + "Asia/Qatar\n", + "Asia/Qostanay\n", + "Asia/Qyzylorda\n", + "Asia/Rangoon\n", + "Asia/Riyadh\n", + "Asia/Saigon\n", + "Asia/Sakhalin\n", + "Asia/Samarkand\n", + "Asia/Seoul\n", + "Asia/Shanghai\n", + "Asia/Singapore\n", + "Asia/Srednekolymsk\n", + "Asia/Taipei\n", + "Asia/Tashkent\n", + "Asia/Tbilisi\n", + "Asia/Tehran\n", + "Asia/Tel_Aviv\n", + "Asia/Thimbu\n", + "Asia/Thimphu\n", + "Asia/Tokyo\n", + "Asia/Tomsk\n", + "Asia/Ujung_Pandang\n", + "Asia/Ulaanbaatar\n", + "Asia/Ulan_Bator\n", + "Asia/Urumqi\n", + "Asia/Ust-Nera\n", + "Asia/Vientiane\n", + "Asia/Vladivostok\n", + "Asia/Yakutsk\n", + "Asia/Yangon\n", + "Asia/Yekaterinburg\n", + "Asia/Yerevan\n", + "Atlantic/Azores\n", + "Atlantic/Bermuda\n", + "Atlantic/Canary\n", + "Atlantic/Cape_Verde\n", + "Atlantic/Faeroe\n", + "Atlantic/Faroe\n", + "Atlantic/Jan_Mayen\n", + "Atlantic/Madeira\n", + "Atlantic/Reykjavik\n", + "Atlantic/South_Georgia\n", + "Atlantic/St_Helena\n", + "Atlantic/Stanley\n", + "Australia/ACT\n", + "Australia/Adelaide\n", + "Australia/Brisbane\n", + "Australia/Broken_Hill\n", + "Australia/Canberra\n", + "Australia/Currie\n", + "Australia/Darwin\n", + "Australia/Eucla\n", + "Australia/Hobart\n", + "Australia/LHI\n", + "Australia/Lindeman\n", + "Australia/Lord_Howe\n", + "Australia/Melbourne\n", + "Australia/NSW\n", + "Australia/North\n", + "Australia/Perth\n", + "Australia/Queensland\n", + "Australia/South\n", + "Australia/Sydney\n", + "Australia/Tasmania\n", + "Australia/Victoria\n", + "Australia/West\n", + "Australia/Yancowinna\n", + "Brazil/Acre\n", + "Brazil/DeNoronha\n", + "Brazil/East\n", + "Brazil/West\n", + "CET\n", + "CST6CDT\n", + "Canada/Atlantic\n", + "Canada/Central\n", + "Canada/Eastern\n", + "Canada/Mountain\n", + "Canada/Newfoundland\n", + "Canada/Pacific\n", + "Canada/Saskatchewan\n", + "Canada/Yukon\n", + "Chile/Continental\n", + "Chile/EasterIsland\n", + "Cuba\n", + "EET\n", + "EST\n", + "EST5EDT\n", + "Egypt\n", + "Eire\n", + "Etc/GMT\n", + "Etc/GMT+0\n", + "Etc/GMT+1\n", + "Etc/GMT+10\n", + "Etc/GMT+11\n", + "Etc/GMT+12\n", + "Etc/GMT+2\n", + "Etc/GMT+3\n", + "Etc/GMT+4\n", + "Etc/GMT+5\n", + "Etc/GMT+6\n", + "Etc/GMT+7\n", + "Etc/GMT+8\n", + "Etc/GMT+9\n", + "Etc/GMT-0\n", + "Etc/GMT-1\n", + "Etc/GMT-10\n", + "Etc/GMT-11\n", + "Etc/GMT-12\n", + "Etc/GMT-13\n", + "Etc/GMT-14\n", + "Etc/GMT-2\n", + "Etc/GMT-3\n", + "Etc/GMT-4\n", + "Etc/GMT-5\n", + "Etc/GMT-6\n", + "Etc/GMT-7\n", + "Etc/GMT-8\n", + "Etc/GMT-9\n", + "Etc/GMT0\n", + "Etc/Greenwich\n", + "Etc/UCT\n", + "Etc/UTC\n", + "Etc/Universal\n", + "Etc/Zulu\n", + "Europe/Amsterdam\n", + "Europe/Andorra\n", + "Europe/Astrakhan\n", + "Europe/Athens\n", + "Europe/Belfast\n", + "Europe/Belgrade\n", + "Europe/Berlin\n", + "Europe/Bratislava\n", + "Europe/Brussels\n", + "Europe/Bucharest\n", + "Europe/Budapest\n", + "Europe/Busingen\n", + "Europe/Chisinau\n", + "Europe/Copenhagen\n", + "Europe/Dublin\n", + "Europe/Gibraltar\n", + "Europe/Guernsey\n", + "Europe/Helsinki\n", + "Europe/Isle_of_Man\n", + "Europe/Istanbul\n", + "Europe/Jersey\n", + "Europe/Kaliningrad\n", + "Europe/Kiev\n", + "Europe/Kirov\n", + "Europe/Lisbon\n", + "Europe/Ljubljana\n", + "Europe/London\n", + "Europe/Luxembourg\n", + "Europe/Madrid\n", + "Europe/Malta\n", + "Europe/Mariehamn\n", + "Europe/Minsk\n", + "Europe/Monaco\n", + "Europe/Moscow\n", + "Europe/Nicosia\n", + "Europe/Oslo\n", + "Europe/Paris\n", + "Europe/Podgorica\n", + "Europe/Prague\n", + "Europe/Riga\n", + "Europe/Rome\n", + "Europe/Samara\n", + "Europe/San_Marino\n", + "Europe/Sarajevo\n", + "Europe/Saratov\n", + "Europe/Simferopol\n", + "Europe/Skopje\n", + "Europe/Sofia\n", + "Europe/Stockholm\n", + "Europe/Tallinn\n", + "Europe/Tirane\n", + "Europe/Tiraspol\n", + "Europe/Ulyanovsk\n", + "Europe/Uzhgorod\n", + "Europe/Vaduz\n", + "Europe/Vatican\n", + "Europe/Vienna\n", + "Europe/Vilnius\n", + "Europe/Volgograd\n", + "Europe/Warsaw\n", + "Europe/Zagreb\n", + "Europe/Zaporozhye\n", + "Europe/Zurich\n", + "GB\n", + "GB-Eire\n", + "GMT\n", + "GMT+0\n", + "GMT-0\n", + "GMT0\n", + "Greenwich\n", + "HST\n", + "Hongkong\n", + "Iceland\n", + "Indian/Antananarivo\n", + "Indian/Chagos\n", + "Indian/Christmas\n", + "Indian/Cocos\n", + "Indian/Comoro\n", + "Indian/Kerguelen\n", + "Indian/Mahe\n", + "Indian/Maldives\n", + "Indian/Mauritius\n", + "Indian/Mayotte\n", + "Indian/Reunion\n", + "Iran\n", + "Israel\n", + "Jamaica\n", + "Japan\n", + "Kwajalein\n", + "Libya\n", + "MET\n", + "MST\n", + "MST7MDT\n", + "Mexico/BajaNorte\n", + "Mexico/BajaSur\n", + "Mexico/General\n", + "NZ\n", + "NZ-CHAT\n", + "Navajo\n", + "PRC\n", + "PST8PDT\n", + "Pacific/Apia\n", + "Pacific/Auckland\n", + "Pacific/Bougainville\n", + "Pacific/Chatham\n", + "Pacific/Chuuk\n", + "Pacific/Easter\n", + "Pacific/Efate\n", + "Pacific/Enderbury\n", + "Pacific/Fakaofo\n", + "Pacific/Fiji\n", + "Pacific/Funafuti\n", + "Pacific/Galapagos\n", + "Pacific/Gambier\n", + "Pacific/Guadalcanal\n", + "Pacific/Guam\n", + "Pacific/Honolulu\n", + "Pacific/Johnston\n", + "Pacific/Kanton\n", + "Pacific/Kiritimati\n", + "Pacific/Kosrae\n", + "Pacific/Kwajalein\n", + "Pacific/Majuro\n", + "Pacific/Marquesas\n", + "Pacific/Midway\n", + "Pacific/Nauru\n", + "Pacific/Niue\n", + "Pacific/Norfolk\n", + "Pacific/Noumea\n", + "Pacific/Pago_Pago\n", + "Pacific/Palau\n", + "Pacific/Pitcairn\n", + "Pacific/Pohnpei\n", + "Pacific/Ponape\n", + "Pacific/Port_Moresby\n", + "Pacific/Rarotonga\n", + "Pacific/Saipan\n", + "Pacific/Samoa\n", + "Pacific/Tahiti\n", + "Pacific/Tarawa\n", + "Pacific/Tongatapu\n", + "Pacific/Truk\n", + "Pacific/Wake\n", + "Pacific/Wallis\n", + "Pacific/Yap\n", + "Poland\n", + "Portugal\n", + "ROC\n", + "ROK\n", + "Singapore\n", + "Turkey\n", + "UCT\n", + "US/Alaska\n", + "US/Aleutian\n", + "US/Arizona\n", + "US/Central\n", + "US/East-Indiana\n", + "US/Eastern\n", + "US/Hawaii\n", + "US/Indiana-Starke\n", + "US/Michigan\n", + "US/Mountain\n", + "US/Pacific\n", + "US/Samoa\n", + "UTC\n", + "Universal\n", + "W-SU\n", + "WET\n", + "Zulu\n" + ] + } + ], + "source": [ + "for tz in pytz.all_timezones:\n", + " print(tz)" + ] + }, + { + "cell_type": "markdown", + "id": "f18fa2c9", + "metadata": {}, + "source": [ + "With the `zoneinfo` module:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "9d2cf9d1", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Africa/Abidjan\n", + "Africa/Accra\n", + "Africa/Addis_Ababa\n", + "Africa/Algiers\n", + "Africa/Asmara\n", + "Africa/Asmera\n", + "Africa/Bamako\n", + "Africa/Bangui\n", + "Africa/Banjul\n", + "Africa/Bissau\n", + "Africa/Blantyre\n", + "Africa/Brazzaville\n", + "Africa/Bujumbura\n", + "Africa/Cairo\n", + "Africa/Casablanca\n", + "Africa/Ceuta\n", + "Africa/Conakry\n", + "Africa/Dakar\n", + "Africa/Dar_es_Salaam\n", + "Africa/Djibouti\n", + "Africa/Douala\n", + "Africa/El_Aaiun\n", + "Africa/Freetown\n", + "Africa/Gaborone\n", + "Africa/Harare\n", + "Africa/Johannesburg\n", + "Africa/Juba\n", + "Africa/Kampala\n", + "Africa/Khartoum\n", + "Africa/Kigali\n", + "Africa/Kinshasa\n", + "Africa/Lagos\n", + "Africa/Libreville\n", + "Africa/Lome\n", + "Africa/Luanda\n", + "Africa/Lubumbashi\n", + "Africa/Lusaka\n", + "Africa/Malabo\n", + "Africa/Maputo\n", + "Africa/Maseru\n", + "Africa/Mbabane\n", + "Africa/Mogadishu\n", + "Africa/Monrovia\n", + "Africa/Nairobi\n", + "Africa/Ndjamena\n", + "Africa/Niamey\n", + "Africa/Nouakchott\n", + "Africa/Ouagadougou\n", + "Africa/Porto-Novo\n", + "Africa/Sao_Tome\n", + "Africa/Timbuktu\n", + "Africa/Tripoli\n", + "Africa/Tunis\n", + "Africa/Windhoek\n", + "America/Adak\n", + "America/Anchorage\n", + "America/Anguilla\n", + "America/Antigua\n", + "America/Araguaina\n", + "America/Argentina/Buenos_Aires\n", + "America/Argentina/Catamarca\n", + "America/Argentina/ComodRivadavia\n", + "America/Argentina/Cordoba\n", + "America/Argentina/Jujuy\n", + "America/Argentina/La_Rioja\n", + "America/Argentina/Mendoza\n", + "America/Argentina/Rio_Gallegos\n", + "America/Argentina/Salta\n", + "America/Argentina/San_Juan\n", + "America/Argentina/San_Luis\n", + "America/Argentina/Tucuman\n", + "America/Argentina/Ushuaia\n", + "America/Aruba\n", + "America/Asuncion\n", + "America/Atikokan\n", + "America/Atka\n", + "America/Bahia\n", + "America/Bahia_Banderas\n", + "America/Barbados\n", + "America/Belem\n", + "America/Belize\n", + "America/Blanc-Sablon\n", + "America/Boa_Vista\n", + "America/Bogota\n", + "America/Boise\n", + "America/Buenos_Aires\n", + "America/Cambridge_Bay\n", + "America/Campo_Grande\n", + "America/Cancun\n", + "America/Caracas\n", + "America/Catamarca\n", + "America/Cayenne\n", + "America/Cayman\n", + "America/Chicago\n", + "America/Chihuahua\n", + "America/Coral_Harbour\n", + "America/Cordoba\n", + "America/Costa_Rica\n", + "America/Creston\n", + "America/Cuiaba\n", + "America/Curacao\n", + "America/Danmarkshavn\n", + "America/Dawson\n", + "America/Dawson_Creek\n", + "America/Denver\n", + "America/Detroit\n", + "America/Dominica\n", + "America/Edmonton\n", + "America/Eirunepe\n", + "America/El_Salvador\n", + "America/Ensenada\n", + "America/Fort_Nelson\n", + "America/Fort_Wayne\n", + "America/Fortaleza\n", + "America/Glace_Bay\n", + "America/Godthab\n", + "America/Goose_Bay\n", + "America/Grand_Turk\n", + "America/Grenada\n", + "America/Guadeloupe\n", + "America/Guatemala\n", + "America/Guayaquil\n", + "America/Guyana\n", + "America/Halifax\n", + "America/Havana\n", + "America/Hermosillo\n", + "America/Indiana/Indianapolis\n", + "America/Indiana/Knox\n", + "America/Indiana/Marengo\n", + "America/Indiana/Petersburg\n", + "America/Indiana/Tell_City\n", + "America/Indiana/Vevay\n", + "America/Indiana/Vincennes\n", + "America/Indiana/Winamac\n", + "America/Indianapolis\n", + "America/Inuvik\n", + "America/Iqaluit\n", + "America/Jamaica\n", + "America/Jujuy\n", + "America/Juneau\n", + "America/Kentucky/Louisville\n", + "America/Kentucky/Monticello\n", + "America/Knox_IN\n", + "America/Kralendijk\n", + "America/La_Paz\n", + "America/Lima\n", + "America/Los_Angeles\n", + "America/Louisville\n", + "America/Lower_Princes\n", + "America/Maceio\n", + "America/Managua\n", + "America/Manaus\n", + "America/Marigot\n", + "America/Martinique\n", + "America/Matamoros\n", + "America/Mazatlan\n", + "America/Mendoza\n", + "America/Menominee\n", + "America/Merida\n", + "America/Metlakatla\n", + "America/Mexico_City\n", + "America/Miquelon\n", + "America/Moncton\n", + "America/Monterrey\n", + "America/Montevideo\n", + "America/Montreal\n", + "America/Montserrat\n", + "America/Nassau\n", + "America/New_York\n", + "America/Nipigon\n", + "America/Nome\n", + "America/Noronha\n", + "America/North_Dakota/Beulah\n", + "America/North_Dakota/Center\n", + "America/North_Dakota/New_Salem\n", + "America/Nuuk\n", + "America/Ojinaga\n", + "America/Panama\n", + "America/Pangnirtung\n", + "America/Paramaribo\n", + "America/Phoenix\n", + "America/Port-au-Prince\n", + "America/Port_of_Spain\n", + "America/Porto_Acre\n", + "America/Porto_Velho\n", + "America/Puerto_Rico\n", + "America/Punta_Arenas\n", + "America/Rainy_River\n", + "America/Rankin_Inlet\n", + "America/Recife\n", + "America/Regina\n", + "America/Resolute\n", + "America/Rio_Branco\n", + "America/Rosario\n", + "America/Santa_Isabel\n", + "America/Santarem\n", + "America/Santiago\n", + "America/Santo_Domingo\n", + "America/Sao_Paulo\n", + "America/Scoresbysund\n", + "America/Shiprock\n", + "America/Sitka\n", + "America/St_Barthelemy\n", + "America/St_Johns\n", + "America/St_Kitts\n", + "America/St_Lucia\n", + "America/St_Thomas\n", + "America/St_Vincent\n", + "America/Swift_Current\n", + "America/Tegucigalpa\n", + "America/Thule\n", + "America/Thunder_Bay\n", + "America/Tijuana\n", + "America/Toronto\n", + "America/Tortola\n", + "America/Vancouver\n", + "America/Virgin\n", + "America/Whitehorse\n", + "America/Winnipeg\n", + "America/Yakutat\n", + "America/Yellowknife\n", + "Antarctica/Casey\n", + "Antarctica/Davis\n", + "Antarctica/DumontDUrville\n", + "Antarctica/Macquarie\n", + "Antarctica/Mawson\n", + "Antarctica/McMurdo\n", + "Antarctica/Palmer\n", + "Antarctica/Rothera\n", + "Antarctica/South_Pole\n", + "Antarctica/Syowa\n", + "Antarctica/Troll\n", + "Antarctica/Vostok\n", + "Arctic/Longyearbyen\n", + "Asia/Aden\n", + "Asia/Almaty\n", + "Asia/Amman\n", + "Asia/Anadyr\n", + "Asia/Aqtau\n", + "Asia/Aqtobe\n", + "Asia/Ashgabat\n", + "Asia/Ashkhabad\n", + "Asia/Atyrau\n", + "Asia/Baghdad\n", + "Asia/Bahrain\n", + "Asia/Baku\n", + "Asia/Bangkok\n", + "Asia/Barnaul\n", + "Asia/Beirut\n", + "Asia/Bishkek\n", + "Asia/Brunei\n", + "Asia/Calcutta\n", + "Asia/Chita\n", + "Asia/Choibalsan\n", + "Asia/Chongqing\n", + "Asia/Chungking\n", + "Asia/Colombo\n", + "Asia/Dacca\n", + "Asia/Damascus\n", + "Asia/Dhaka\n", + "Asia/Dili\n", + "Asia/Dubai\n", + "Asia/Dushanbe\n", + "Asia/Famagusta\n", + "Asia/Gaza\n", + "Asia/Harbin\n", + "Asia/Hebron\n", + "Asia/Ho_Chi_Minh\n", + "Asia/Hong_Kong\n", + "Asia/Hovd\n", + "Asia/Irkutsk\n", + "Asia/Istanbul\n", + "Asia/Jakarta\n", + "Asia/Jayapura\n", + "Asia/Jerusalem\n", + "Asia/Kabul\n", + "Asia/Kamchatka\n", + "Asia/Karachi\n", + "Asia/Kashgar\n", + "Asia/Kathmandu\n", + "Asia/Katmandu\n", + "Asia/Khandyga\n", + "Asia/Kolkata\n", + "Asia/Krasnoyarsk\n", + "Asia/Kuala_Lumpur\n", + "Asia/Kuching\n", + "Asia/Kuwait\n", + "Asia/Macao\n", + "Asia/Macau\n", + "Asia/Magadan\n", + "Asia/Makassar\n", + "Asia/Manila\n", + "Asia/Muscat\n", + "Asia/Nicosia\n", + "Asia/Novokuznetsk\n", + "Asia/Novosibirsk\n", + "Asia/Omsk\n", + "Asia/Oral\n", + "Asia/Phnom_Penh\n", + "Asia/Pontianak\n", + "Asia/Pyongyang\n", + "Asia/Qatar\n", + "Asia/Qostanay\n", + "Asia/Qyzylorda\n", + "Asia/Rangoon\n", + "Asia/Riyadh\n", + "Asia/Saigon\n", + "Asia/Sakhalin\n", + "Asia/Samarkand\n", + "Asia/Seoul\n", + "Asia/Shanghai\n", + "Asia/Singapore\n", + "Asia/Srednekolymsk\n", + "Asia/Taipei\n", + "Asia/Tashkent\n", + "Asia/Tbilisi\n", + "Asia/Tehran\n", + "Asia/Tel_Aviv\n", + "Asia/Thimbu\n", + "Asia/Thimphu\n", + "Asia/Tokyo\n", + "Asia/Tomsk\n", + "Asia/Ujung_Pandang\n", + "Asia/Ulaanbaatar\n", + "Asia/Ulan_Bator\n", + "Asia/Urumqi\n", + "Asia/Ust-Nera\n", + "Asia/Vientiane\n", + "Asia/Vladivostok\n", + "Asia/Yakutsk\n", + "Asia/Yangon\n", + "Asia/Yekaterinburg\n", + "Asia/Yerevan\n", + "Atlantic/Azores\n", + "Atlantic/Bermuda\n", + "Atlantic/Canary\n", + "Atlantic/Cape_Verde\n", + "Atlantic/Faeroe\n", + "Atlantic/Faroe\n", + "Atlantic/Jan_Mayen\n", + "Atlantic/Madeira\n", + "Atlantic/Reykjavik\n", + "Atlantic/South_Georgia\n", + "Atlantic/St_Helena\n", + "Atlantic/Stanley\n", + "Australia/ACT\n", + "Australia/Adelaide\n", + "Australia/Brisbane\n", + "Australia/Broken_Hill\n", + "Australia/Canberra\n", + "Australia/Currie\n", + "Australia/Darwin\n", + "Australia/Eucla\n", + "Australia/Hobart\n", + "Australia/LHI\n", + "Australia/Lindeman\n", + "Australia/Lord_Howe\n", + "Australia/Melbourne\n", + "Australia/NSW\n", + "Australia/North\n", + "Australia/Perth\n", + "Australia/Queensland\n", + "Australia/South\n", + "Australia/Sydney\n", + "Australia/Tasmania\n", + "Australia/Victoria\n", + "Australia/West\n", + "Australia/Yancowinna\n", + "Brazil/Acre\n", + "Brazil/DeNoronha\n", + "Brazil/East\n", + "Brazil/West\n", + "CET\n", + "CST6CDT\n", + "Canada/Atlantic\n", + "Canada/Central\n", + "Canada/Eastern\n", + "Canada/Mountain\n", + "Canada/Newfoundland\n", + "Canada/Pacific\n", + "Canada/Saskatchewan\n", + "Canada/Yukon\n", + "Chile/Continental\n", + "Chile/EasterIsland\n", + "Cuba\n", + "EET\n", + "EST\n", + "EST5EDT\n", + "Egypt\n", + "Eire\n", + "Etc/GMT\n", + "Etc/GMT+0\n", + "Etc/GMT+1\n", + "Etc/GMT+10\n", + "Etc/GMT+11\n", + "Etc/GMT+12\n", + "Etc/GMT+2\n", + "Etc/GMT+3\n", + "Etc/GMT+4\n", + "Etc/GMT+5\n", + "Etc/GMT+6\n", + "Etc/GMT+7\n", + "Etc/GMT+8\n", + "Etc/GMT+9\n", + "Etc/GMT-0\n", + "Etc/GMT-1\n", + "Etc/GMT-10\n", + "Etc/GMT-11\n", + "Etc/GMT-12\n", + "Etc/GMT-13\n", + "Etc/GMT-14\n", + "Etc/GMT-2\n", + "Etc/GMT-3\n", + "Etc/GMT-4\n", + "Etc/GMT-5\n", + "Etc/GMT-6\n", + "Etc/GMT-7\n", + "Etc/GMT-8\n", + "Etc/GMT-9\n", + "Etc/GMT0\n", + "Etc/Greenwich\n", + "Etc/UCT\n", + "Etc/UTC\n", + "Etc/Universal\n", + "Etc/Zulu\n", + "Europe/Amsterdam\n", + "Europe/Andorra\n", + "Europe/Astrakhan\n", + "Europe/Athens\n", + "Europe/Belfast\n", + "Europe/Belgrade\n", + "Europe/Berlin\n", + "Europe/Bratislava\n", + "Europe/Brussels\n", + "Europe/Bucharest\n", + "Europe/Budapest\n", + "Europe/Busingen\n", + "Europe/Chisinau\n", + "Europe/Copenhagen\n", + "Europe/Dublin\n", + "Europe/Gibraltar\n", + "Europe/Guernsey\n", + "Europe/Helsinki\n", + "Europe/Isle_of_Man\n", + "Europe/Istanbul\n", + "Europe/Jersey\n", + "Europe/Kaliningrad\n", + "Europe/Kiev\n", + "Europe/Kirov\n", + "Europe/Lisbon\n", + "Europe/Ljubljana\n", + "Europe/London\n", + "Europe/Luxembourg\n", + "Europe/Madrid\n", + "Europe/Malta\n", + "Europe/Mariehamn\n", + "Europe/Minsk\n", + "Europe/Monaco\n", + "Europe/Moscow\n", + "Europe/Nicosia\n", + "Europe/Oslo\n", + "Europe/Paris\n", + "Europe/Podgorica\n", + "Europe/Prague\n", + "Europe/Riga\n", + "Europe/Rome\n", + "Europe/Samara\n", + "Europe/San_Marino\n", + "Europe/Sarajevo\n", + "Europe/Saratov\n", + "Europe/Simferopol\n", + "Europe/Skopje\n", + "Europe/Sofia\n", + "Europe/Stockholm\n", + "Europe/Tallinn\n", + "Europe/Tirane\n", + "Europe/Tiraspol\n", + "Europe/Ulyanovsk\n", + "Europe/Uzhgorod\n", + "Europe/Vaduz\n", + "Europe/Vatican\n", + "Europe/Vienna\n", + "Europe/Vilnius\n", + "Europe/Volgograd\n", + "Europe/Warsaw\n", + "Europe/Zagreb\n", + "Europe/Zaporozhye\n", + "Europe/Zurich\n", + "Factory\n", + "GB\n", + "GB-Eire\n", + "GMT\n", + "GMT+0\n", + "GMT-0\n", + "GMT0\n", + "Greenwich\n", + "HST\n", + "Hongkong\n", + "Iceland\n", + "Indian/Antananarivo\n", + "Indian/Chagos\n", + "Indian/Christmas\n", + "Indian/Cocos\n", + "Indian/Comoro\n", + "Indian/Kerguelen\n", + "Indian/Mahe\n", + "Indian/Maldives\n", + "Indian/Mauritius\n", + "Indian/Mayotte\n", + "Indian/Reunion\n", + "Iran\n", + "Israel\n", + "Jamaica\n", + "Japan\n", + "Kwajalein\n", + "Libya\n", + "MET\n", + "MST\n", + "MST7MDT\n", + "Mexico/BajaNorte\n", + "Mexico/BajaSur\n", + "Mexico/General\n", + "NZ\n", + "NZ-CHAT\n", + "Navajo\n", + "PRC\n", + "PST8PDT\n", + "Pacific/Apia\n", + "Pacific/Auckland\n", + "Pacific/Bougainville\n", + "Pacific/Chatham\n", + "Pacific/Chuuk\n", + "Pacific/Easter\n", + "Pacific/Efate\n", + "Pacific/Enderbury\n", + "Pacific/Fakaofo\n", + "Pacific/Fiji\n", + "Pacific/Funafuti\n", + "Pacific/Galapagos\n", + "Pacific/Gambier\n", + "Pacific/Guadalcanal\n", + "Pacific/Guam\n", + "Pacific/Honolulu\n", + "Pacific/Johnston\n", + "Pacific/Kanton\n", + "Pacific/Kiritimati\n", + "Pacific/Kosrae\n", + "Pacific/Kwajalein\n", + "Pacific/Majuro\n", + "Pacific/Marquesas\n", + "Pacific/Midway\n", + "Pacific/Nauru\n", + "Pacific/Niue\n", + "Pacific/Norfolk\n", + "Pacific/Noumea\n", + "Pacific/Pago_Pago\n", + "Pacific/Palau\n", + "Pacific/Pitcairn\n", + "Pacific/Pohnpei\n", + "Pacific/Ponape\n", + "Pacific/Port_Moresby\n", + "Pacific/Rarotonga\n", + "Pacific/Saipan\n", + "Pacific/Samoa\n", + "Pacific/Tahiti\n", + "Pacific/Tarawa\n", + "Pacific/Tongatapu\n", + "Pacific/Truk\n", + "Pacific/Wake\n", + "Pacific/Wallis\n", + "Pacific/Yap\n", + "Poland\n", + "Portugal\n", + "ROC\n", + "ROK\n", + "Singapore\n", + "Turkey\n", + "UCT\n", + "US/Alaska\n", + "US/Aleutian\n", + "US/Arizona\n", + "US/Central\n", + "US/East-Indiana\n", + "US/Eastern\n", + "US/Hawaii\n", + "US/Indiana-Starke\n", + "US/Michigan\n", + "US/Mountain\n", + "US/Pacific\n", + "US/Samoa\n", + "UTC\n", + "Universal\n", + "W-SU\n", + "WET\n", + "Zulu\n" + ] + } + ], + "source": [ + "for tz in sorted(zoneinfo.available_timezones()):\n", + " print(tz)" + ] + }, + { + "cell_type": "markdown", + "id": "6a1887cf", + "metadata": {}, + "source": [ + "Are the time zones defined by `pytz` and `zoneinfo` the same? Yes!" + ] + }, + { + "cell_type": "markdown", + "id": "8f8df276", + "metadata": {}, + "source": [ + "In this example, let's take our current time in UTC, and convert it to some other time zone, say `Australia/Melbourne`." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "77f1b8d9", + "metadata": {}, + "outputs": [], + "source": [ + "now_utc_naive = datetime.utcnow()" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "f8586215", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "datetime.datetime(2022, 3, 20, 6, 1, 3, 368403)" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "now_utc_naive" + ] + }, + { + "cell_type": "markdown", + "id": "a452220a", + "metadata": {}, + "source": [ + "The problem here is that we have a _naive_ datetime (i.e. one without an attached timezone)." + ] + }, + { + "cell_type": "markdown", + "id": "d1d10809", + "metadata": {}, + "source": [ + "We can make this naive datetime time zone aware by tacking on the timezone (since we know it is UTC):" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "db60736b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "datetime.datetime(2022, 3, 20, 6, 1, 3, 368403, tzinfo=datetime.timezone.utc)" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "now_utc_aware = now_utc_naive.replace(tzinfo=timezone.utc)\n", + "now_utc_aware" + ] + }, + { + "cell_type": "markdown", + "id": "8282b097", + "metadata": {}, + "source": [ + "Or, we could use the `pytz` library to do the same thing:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "0d7434f7", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "datetime.datetime(2022, 3, 20, 6, 1, 3, 377626, tzinfo=)" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "pytz.utc.localize(datetime.utcnow())" + ] + }, + { + "cell_type": "markdown", + "id": "e613c2e1", + "metadata": {}, + "source": [ + "Now that we have a time zone aware datetime, we can convert it to another timezone using `pytz`:" + ] + }, + { + "cell_type": "markdown", + "id": "94b0b5c9", + "metadata": {}, + "source": [ + "First, let's pick a time zone from `pytz`:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "833b5ff9", + "metadata": {}, + "outputs": [], + "source": [ + "tz_melbourne = pytz.timezone('Australia/Melbourne')" + ] + }, + { + "cell_type": "markdown", + "id": "ce4323c1", + "metadata": {}, + "source": [ + "And now we localize our aware datetime to this time zone:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "d23fd3c4", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "datetime.datetime(2022, 3, 20, 17, 1, 3, 368403, tzinfo=)" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "now_utc_aware.astimezone(tz_melbourne)" + ] + }, + { + "cell_type": "markdown", + "id": "260bd7ff", + "metadata": {}, + "source": [ + "We could do both these steps in a single expression:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "6559daf7", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "datetime.datetime(2022, 3, 20, 17, 1, 3, 368403, tzinfo=)" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "now_utc_aware.astimezone(pytz.timezone('Australia/Melbourne'))" + ] + }, + { + "cell_type": "markdown", + "id": "6be99718", + "metadata": {}, + "source": [ + "Now, let's do the same thing using the `zoneinfo` module." + ] + }, + { + "cell_type": "markdown", + "id": "8c9b22bb", + "metadata": {}, + "source": [ + "Let's pick the same target time zone:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "30c58db3", + "metadata": {}, + "outputs": [], + "source": [ + "tz_zi_dublin = ZoneInfo(\"Europe/Dublin\")" + ] + }, + { + "cell_type": "markdown", + "id": "0f77b5b0", + "metadata": {}, + "source": [ + "And the let's convert our aware datetime to that time zone:" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "1623322d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "datetime.datetime(2022, 3, 20, 6, 1, 3, 368403, tzinfo=zoneinfo.ZoneInfo(key='Europe/Dublin'))" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "now_utc_aware.astimezone(tz_zi_dublin)" + ] + }, + { + "cell_type": "markdown", + "id": "4536470f", + "metadata": {}, + "source": [ + "Or, we can also write this as a single expression:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "337c3955", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "datetime.datetime(2022, 3, 20, 6, 1, 3, 368403, tzinfo=zoneinfo.ZoneInfo(key='Europe/Dublin'))" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "now_utc_aware.astimezone(ZoneInfo(\"Europe/Dublin\"))" + ] + }, + { + "cell_type": "markdown", + "id": "4076195d", + "metadata": {}, + "source": [ + "#### The `math` Module" + ] + }, + { + "cell_type": "markdown", + "id": "41d86e28", + "metadata": {}, + "source": [ + "Several enhancements or additions have been to the math library." + ] + }, + { + "cell_type": "markdown", + "id": "bd3de95f", + "metadata": {}, + "source": [ + "The `math` module already had the `gcd` function to calculate the great common divisor of two numbers:" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "911df27e", + "metadata": {}, + "outputs": [], + "source": [ + "import math" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "4154629e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "9" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "math.gcd(27, 45)" + ] + }, + { + "cell_type": "markdown", + "id": "85abe1fa", + "metadata": {}, + "source": [ + "But now `gcd` can take multiple arguments, not just two:" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "d64ea0d9", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "math.gcd(27, 45, 18, 15)" + ] + }, + { + "cell_type": "markdown", + "id": "0503d78a", + "metadata": {}, + "source": [ + "The `lcm` (least common multiple) function has been added:" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "b4c25fef", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "12" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "math.lcm(2, 3, 4)" + ] + }, + { + "cell_type": "markdown", + "id": "58a4c3cd", + "metadata": {}, + "source": [ + "#### Dictionary Unions" + ] + }, + { + "cell_type": "markdown", + "id": "2c05f130", + "metadata": {}, + "source": [ + "When we discussed dictionaries in this course, we saw that we could combine two dictionaries using unpacking:" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "136c8836", + "metadata": {}, + "outputs": [], + "source": [ + "d1 = {'a': 1, 'b': 2, 'c': 3}\n", + "d2 = {'c': 30, 'd': 40}" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "81331802", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'a': 1, 'b': 2, 'c': 30, 'd': 40}" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "{**d1, **d2}" + ] + }, + { + "cell_type": "markdown", + "id": "97ca030f", + "metadata": {}, + "source": [ + "As we saw the second dictionary's key/value pair \"overwrote\" the key/value pair from the first dictionary." + ] + }, + { + "cell_type": "markdown", + "id": "b361771e", + "metadata": {}, + "source": [ + "We could also use the `ChainMap` function in the `collections` module:" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "d633f16c", + "metadata": {}, + "outputs": [], + "source": [ + "from collections import ChainMap" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "ad250dfd", + "metadata": {}, + "outputs": [], + "source": [ + "merged = ChainMap(d1, d2)" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "1deeb8f2", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(1, 3, 40)" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "merged['a'], merged['c'], merged['d']" + ] + }, + { + "cell_type": "markdown", + "id": "1751d47a", + "metadata": {}, + "source": [ + "As you can see, in the `ChainMap`, the firest occurrence of the key is used - so in this case `c` comes from `d1`, not `d2`." + ] + }, + { + "cell_type": "markdown", + "id": "1c63e893", + "metadata": {}, + "source": [ + "Both of these ways of \"combining\" dictionaries work well - but they are not very intuitive, and need a little attention to what happens when you have common keys in the dictionaries." + ] + }, + { + "cell_type": "markdown", + "id": "aca5787b", + "metadata": {}, + "source": [ + "Think of concatenating lists where we can simply use the `+` operator - this is very intuitive:" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "de850747", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[1, 2, 3, 4, 5, 6]" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "[1, 2, 3] + [4, 5, 6]" + ] + }, + { + "cell_type": "markdown", + "id": "69429bab", + "metadata": {}, + "source": [ + "Now dictionaries are not like lists, but they are closely related to **sets**. With sets, we have the **union** operator (`|`):" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "dcb8a7b5", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'a', 'b', 'c', 'd'}" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "s1 = {'a', 'b', 'c'}\n", + "s2 = {'c', 'd'}\n", + "\n", + "s1 | s2" + ] + }, + { + "cell_type": "markdown", + "id": "32c20dfb", + "metadata": {}, + "source": [ + "Python 3.9 introduces support for the **union** (`|`) operation between dictionaries as well." + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "af3fcc01", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'a': 1, 'b': 2, 'c': 30, 'd': 40}" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "d1 | d2" + ] + }, + { + "cell_type": "markdown", + "id": "851ffadc", + "metadata": {}, + "source": [ + "Just like with the `{**d1, **d2}` approach, the value for `c` came from the second dictionary." + ] + }, + { + "cell_type": "markdown", + "id": "cb6fc060", + "metadata": {}, + "source": [ + "And just like with that technique we can control this by switching the order of the dictionaries in the union:" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "05555100", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'c': 3, 'd': 40, 'a': 1, 'b': 2}" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "d2 | d1" + ] + }, + { + "cell_type": "markdown", + "id": "739e210f", + "metadata": {}, + "source": [ + "One question we should have, is what happens to the insertion order that Python dictionaries now guarantee?" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "f8759da5", + "metadata": {}, + "outputs": [], + "source": [ + "d1 = {'c': 3, 'a': 1, 'b': 2}\n", + "d2 = {'d': 40, 'c': 30}" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "e06e3966", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'c': 30, 'a': 1, 'b': 2, 'd': 40}" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "d1 | d2" + ] + }, + { + "cell_type": "markdown", + "id": "8e0e1c2e", + "metadata": {}, + "source": [ + "As you can see, even though the **value** for `c` came from the **second** dictionary, the original inertion order of the **keys** is maintained, so `c` is still in first position in the union of the two dictionaries." + ] + }, + { + "cell_type": "markdown", + "id": "e12dee6a", + "metadata": {}, + "source": [ + "#### String Methods" + ] + }, + { + "cell_type": "markdown", + "id": "f69f79e4", + "metadata": {}, + "source": [ + "Often we need to remove some prefix or suffix in a string." + ] + }, + { + "cell_type": "markdown", + "id": "67496793", + "metadata": {}, + "source": [ + "For example, we may have this list of string:" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "9da4cabe", + "metadata": {}, + "outputs": [], + "source": [ + "data = [\n", + " \"(log) [2022-03-01T13:30:01] Log record 1\",\n", + " \"(log) [2022-03-01T13:30:02] Log record 2\",\n", + " \"(log) [2022-03-01T13:30:03] Log record 3\",\n", + " \"(log) [2022-03-01T13:30:04] Log record 4\",\n", + "]" + ] + }, + { + "cell_type": "markdown", + "id": "30de1de8", + "metadata": {}, + "source": [ + "And we want to clean these up and remove the `(log) ` prefix (including the space)." + ] + }, + { + "cell_type": "markdown", + "id": "84a6c502", + "metadata": {}, + "source": [ + "We can certainly do it this way:" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "8a60ecdc", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['[2022-03-01T13:30:01] Log record 1',\n", + " '[2022-03-01T13:30:02] Log record 2',\n", + " '[2022-03-01T13:30:03] Log record 3',\n", + " '[2022-03-01T13:30:04] Log record 4']" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "clean = [\n", + " s.replace(\"(log) \", '')\n", + " for s in data\n", + "]\n", + "clean" + ] + }, + { + "cell_type": "markdown", + "id": "45fa082e", + "metadata": {}, + "source": [ + "You might be tempted to use the `lstrip` method:" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "id": "74e60a43", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['[2022-03-01T13:30:01] Log record 1',\n", + " '[2022-03-01T13:30:02] Log record 2',\n", + " '[2022-03-01T13:30:03] Log record 3',\n", + " '[2022-03-01T13:30:04] Log record 4']" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "clean = [\n", + " s.lstrip(\"(log) \")\n", + " for s in data\n", + "]\n", + "clean" + ] + }, + { + "cell_type": "markdown", + "id": "5e045ebc", + "metadata": {}, + "source": [ + "This appears to work, but `lstrip` (and `rstrip`) does not interpet `\"(log )\"` as a string, but rather a **sequence** of characters, and each one will be removed - so you might end up with this problem:" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "id": "ddc6e3a8", + "metadata": {}, + "outputs": [], + "source": [ + "data2 = [\n", + " \"(log) log: [2022-03-01T13:30:01] Log record 1\",\n", + " \"(log) log: [2022-03-01T13:30:02] Log record 2\",\n", + " \"(log) log: [2022-03-01T13:30:03] Log record 3\",\n", + " \"(log) log: [2022-03-01T13:30:04] Log record 4\",\n", + "]" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "id": "d87cd3c5", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[': [2022-03-01T13:30:01] Log record 1',\n", + " ': [2022-03-01T13:30:02] Log record 2',\n", + " ': [2022-03-01T13:30:03] Log record 3',\n", + " ': [2022-03-01T13:30:04] Log record 4']" + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "clean = [\n", + " s.lstrip(\"(log) \")\n", + " for s in data2\n", + "]\n", + "clean" + ] + }, + { + "cell_type": "markdown", + "id": "0f73aac3", + "metadata": {}, + "source": [ + "Now that removed a lot more than expected everything from those strings, unlike the replace, which will replace only the first occurrence by default:" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "id": "eaf8792b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['log: [2022-03-01T13:30:01] Log record 1',\n", + " 'log: [2022-03-01T13:30:02] Log record 2',\n", + " 'log: [2022-03-01T13:30:03] Log record 3',\n", + " 'log: [2022-03-01T13:30:04] Log record 4']" + ] + }, + "execution_count": 34, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "clean = [\n", + " s.replace(\"(log) \", '')\n", + " for s in data2\n", + "]\n", + "clean" + ] + }, + { + "cell_type": "markdown", + "id": "0aca0211", + "metadata": {}, + "source": [ + "Python 3,9 introduces two new string methods to do this without having to use `replace`, namely the `removeprefix()` and `removesuffix()` methods:" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "id": "b320eb46", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['[2022-03-01T13:30:01] Log record 1',\n", + " '[2022-03-01T13:30:02] Log record 2',\n", + " '[2022-03-01T13:30:03] Log record 3',\n", + " '[2022-03-01T13:30:04] Log record 4']" + ] + }, + "execution_count": 35, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "[\n", + " s.removeprefix(\"(log) \")\n", + " for s in data\n", + "]" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "id": "d4ebaf46", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['log: [2022-03-01T13:30:01] Log record 1',\n", + " 'log: [2022-03-01T13:30:02] Log record 2',\n", + " 'log: [2022-03-01T13:30:03] Log record 3',\n", + " 'log: [2022-03-01T13:30:04] Log record 4']" + ] + }, + "execution_count": 36, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "[\n", + " s.removeprefix(\"(log) \")\n", + " for s in data2\n", + "]" + ] + }, + { + "cell_type": "markdown", + "id": "f7a2ffd4", + "metadata": {}, + "source": [ + "Note that if the prefix (or suffix) is not found, nothing happens, the new string will be the same as the original (i.e. no exception is raised):" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "id": "829cb63b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'Python rocks!'" + ] + }, + "execution_count": 37, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "'Python rocks!'.removeprefix('Java')" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.10.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Part 4/Section 15 - Extras/03 - Relevant Python 3.10 Changes.ipynb b/Part 4/Section 15 - Extras/03 - Relevant Python 3.10 Changes.ipynb new file mode 100644 index 00000000..1e7fa9b4 --- /dev/null +++ b/Part 4/Section 15 - Extras/03 - Relevant Python 3.10 Changes.ipynb @@ -0,0 +1,991 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "c4d4fc49", + "metadata": {}, + "source": [ + "### Relevant Python 3.10 Changes" + ] + }, + { + "cell_type": "markdown", + "id": "cb1495a9", + "metadata": {}, + "source": [ + "The release of Python 3.10 has brought some new features.\n", + "\n", + "This is a summary of the ones _I_ deemed relevant to this course, and does **not** include all the changes!\n", + "\n", + "For full release details, see [here](https://docs.python.org/3/whatsnew/3.10.html)" + ] + }, + { + "cell_type": "markdown", + "id": "98c3ae0d", + "metadata": {}, + "source": [ + "Python 3.10 has improved it's error messages for syntax errors - the messages are more helpful and hopefully give you a better understanding of what may be syntactically wrong with your code." + ] + }, + { + "cell_type": "markdown", + "id": "4ae4aa37", + "metadata": {}, + "source": [ + "#### Structural Pattern Matching" + ] + }, + { + "cell_type": "markdown", + "id": "d18177a2", + "metadata": {}, + "source": [ + "One thing I often hear people ask, is, what's the Python equivalent of a `switch` statement." + ] + }, + { + "cell_type": "markdown", + "id": "36a77514", + "metadata": {}, + "source": [ + "Until now, the answer has alwasy been - there isn't one. Use `if...elif` constructs." + ] + }, + { + "cell_type": "markdown", + "id": "56fdb138", + "metadata": {}, + "source": [ + "Python 3.10 introduces a new language element (`match`) to implement something called **pattern matching**, that can be used to replicate this `switch` behavior you might be used to in other languages." + ] + }, + { + "cell_type": "markdown", + "id": "e1cf8b9d", + "metadata": {}, + "source": [ + "I'll cover some of the basics here, but you should refer to the Python [docs](https://docs.python.org/3/reference/compound_stmts.html#the-match-statement) for more information, as well as the [pep](https://peps.python.org/pep-0634/) for this feature and a [tutorial pep](https://peps.python.org/pep-0636/)." + ] + }, + { + "cell_type": "markdown", + "id": "1417630a", + "metadata": {}, + "source": [ + "Let's start with a simple `match` statement:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "bbecc554", + "metadata": {}, + "outputs": [], + "source": [ + "def respond(language):\n", + " match language:\n", + " case \"Java\":\n", + " return \"Hmm, coffee!\"\n", + " case \"Python\":\n", + " return \"I'm not scared of snakes!\"\n", + " case \"Rust\":\n", + " return \"Don't drink too much water!\"\n", + " case \"Go\":\n", + " return \"Collect $200\"\n", + " case _:\n", + " return \"I'm sorry...\"" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "d5669e92", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"I'm not scared of snakes!\"" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "respond(\"Python\")" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "90cecc86", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'Collect $200'" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "respond(\"Go\")" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "f9feb023", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"I'm sorry...\"" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "respond(\"COBOL\")" + ] + }, + { + "cell_type": "markdown", + "id": "ee7c21fb", + "metadata": {}, + "source": [ + "Here we were able to define a \"default\" match pattern by using the underscore (`_`) as our pattern - this `_` is called a **wildcard**." + ] + }, + { + "cell_type": "markdown", + "id": "b76575de", + "metadata": {}, + "source": [ + "So this is very much like the \"plain\" switch statement found in some other languages." + ] + }, + { + "cell_type": "markdown", + "id": "6e55dd9f", + "metadata": {}, + "source": [ + "But, this is where things get ineteresting, pattern matching can do much more than the simple example we just saw." + ] + }, + { + "cell_type": "markdown", + "id": "f8d495f2", + "metadata": {}, + "source": [ + "For example, you can have multiple pattern matching:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "6d5fcc0c", + "metadata": {}, + "outputs": [], + "source": [ + "def respond(language):\n", + " match language:\n", + " case \"Java\" | \"Javascript\":\n", + " return \"Love those braces!\"\n", + " case \"Python\":\n", + " return \"I'm a lumberjack and I don't need no braces\"\n", + " case _:\n", + " return \"I have no clue!\"" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "02cf21af", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'Love those braces!'" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "respond(\"Java\")" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "853368f3", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'Love those braces!'" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "respond(\"Javascript\")" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "066f985e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"I'm a lumberjack and I don't need no braces\"" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "respond(\"Python\")" + ] + }, + { + "cell_type": "markdown", + "id": "7fc43e7f", + "metadata": {}, + "source": [ + "We could match against one or more literals by using the OR pattern (`|`)" + ] + }, + { + "cell_type": "markdown", + "id": "36064a9c", + "metadata": {}, + "source": [ + "Let's look at one more example, this time matching **multiple values**." + ] + }, + { + "cell_type": "markdown", + "id": "31978910", + "metadata": {}, + "source": [ + "Suppose we have some kind of command language for driving a remote controlled robot in a maze, picking up and dropping items as it moves around. Our robot is very simple, it can move in only a few directions, and one step at a time. So to move forward three spaces, we would issue three `move forward` commands.\n", + "\n", + "Additional commands are `move backward`, `move left`, `move right`. We also have a few other commands our robot understands: `pick` and `drop` for picking up and dropping objects it might find." + ] + }, + { + "cell_type": "markdown", + "id": "63944ea5", + "metadata": {}, + "source": [ + "We might write a command interpreter this way:" + ] + }, + { + "cell_type": "markdown", + "id": "f9077f13", + "metadata": {}, + "source": [ + "Let's start by using some symbols to represent the robot's actions:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "2dde5d86", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'F': '→', 'B': '←', 'L': '↑', 'R': '↓', 'pick': '⤣', 'drop': '⤥'}" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "symbols = {\n", + " \"F\": \"\\u2192\", \n", + " \"B\": \"\\u2190\", \n", + " \"L\": \"\\u2191\", \n", + " \"R\": \"\\u2193\", \n", + " \"pick\": \"\\u2923\", \n", + " \"drop\": \"\\u2925\"\n", + "}\n", + "\n", + "symbols" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "faab4c09", + "metadata": {}, + "outputs": [], + "source": [ + "def op(command):\n", + " match command:\n", + " case \"move F\":\n", + " return symbols[\"F\"]\n", + " case \"move B\":\n", + " return symbols[\"B\"]\n", + " case \"move L\":\n", + " return symbols[\"L\"]\n", + " case \"move R\":\n", + " return symbols[\"R\"]\n", + " case \"pick\":\n", + " return symbols[\"pick\"]\n", + " case \"drop\":\n", + " return symbols[\"drop\"]\n", + " case _:\n", + " raise ValueError(f\"{command} does not compute!\")" + ] + }, + { + "cell_type": "markdown", + "id": "24555fab", + "metadata": {}, + "source": [ + "Then we could issue commands such as:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "b673a044", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'↑'" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "op(\"move L\")" + ] + }, + { + "cell_type": "markdown", + "id": "b411438f", + "metadata": {}, + "source": [ + "Or multiple sequences by maybe using a list of such commands, effectively creating a sequential program for our robot:" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "cf94014f", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['→', '→', '↑', '⤣', '↓', '↑', '→', '⤥']" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "[\n", + " op(\"move F\"),\n", + " op(\"move F\"),\n", + " op(\"move L\"),\n", + " op(\"pick\"),\n", + " op(\"move R\"),\n", + " op(\"move L\"),\n", + " op(\"move F\"),\n", + " op(\"drop\"),\n", + "]" + ] + }, + { + "cell_type": "markdown", + "id": "c520053b", + "metadata": {}, + "source": [ + "We could use something called **capturing** matched sub-patterns to simply our code somewhat:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "e7da20cf", + "metadata": {}, + "outputs": [], + "source": [ + "def op(command):\n", + " match command:\n", + " case [\"move\", (\"F\" | \"B\" | \"L\" |\"R\") as direction]:\n", + " return symbols[direction]\n", + " case \"pick\":\n", + " return symbols[\"pick\"]\n", + " case \"drop\":\n", + " return symvols[\"drop\"]\n", + " case _:\n", + " raise ValueError(f\"{command} does not compute!\")" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "c2a1c617", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'↑'" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "op([\"move\", \"L\"])" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "4edeb3dc", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'⤣'" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "op(\"pick\")" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "b13504aa", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "fly does not compute!\n" + ] + } + ], + "source": [ + "try:\n", + " op(\"fly\")\n", + "except ValueError as ex:\n", + " print(ex)" + ] + }, + { + "cell_type": "markdown", + "id": "6769c228", + "metadata": {}, + "source": [ + "This is kind of tedious, it would be nicer to write commands such as `move F F L` and `move R L F` instead." + ] + }, + { + "cell_type": "markdown", + "id": "055d2c60", + "metadata": {}, + "source": [ + "There are many ways we could solve this, but pattern matching on multiple values can be really useful here." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "372bdb17", + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "def op(command):\n", + " match command:\n", + " case ['move', *directions]:\n", + " return tuple(symbols[direction] for direction in directions)\n", + " case \"pick\":\n", + " return symbols[\"pick\"]\n", + " case \"drop\":\n", + " return symbols[\"drop\"]\n", + " case _:\n", + " raise ValueError(f\"{command} does not compute!\")" + ] + }, + { + "cell_type": "markdown", + "id": "c77bec44", + "metadata": {}, + "source": [ + "What happens here is that the pattern matcher will recognize the first word `move` and then interpret the remaining words collection them in the `directions` variable (so this syntax is very similar to unpacking)." + ] + }, + { + "cell_type": "markdown", + "id": "4ef78058", + "metadata": {}, + "source": [ + "We can now rewrite our program this way:" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "88c0c024", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[('→', '→', '↑'), '⤣', ('↓', '↑', '→'), '⤥']" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "[\n", + " op([\"move\", \"F\", \"F\", \"L\"]),\n", + " op(\"pick\"),\n", + " op([\"move\", \"R\", \"L\", \"F\"]),\n", + " op(\"drop\"),\n", + "]" + ] + }, + { + "cell_type": "markdown", + "id": "1ddc0f2e", + "metadata": {}, + "source": [ + "But now we have a slight problem:" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "ed30a762", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " 'up'\n" + ] + } + ], + "source": [ + "try:\n", + " op([\"move\", \"up\"])\n", + "except Exception as ex:\n", + " print(type(ex), ex)" + ] + }, + { + "cell_type": "markdown", + "id": "f6bbbd53", + "metadata": {}, + "source": [ + "We would rather just get our custom `ValueError`. To do this we can place a **guard** on our `case` for the `move` command, that will not only do the match but also test an additional condition:" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "8d441175", + "metadata": {}, + "outputs": [], + "source": [ + "def op(command):\n", + " match command:\n", + " case ['move', *directions] if set(directions) < symbols.keys():\n", + " return tuple(symbols[direction] for direction in directions)\n", + " case \"pick\":\n", + " return symbols[\"pick\"]\n", + " case \"drop\":\n", + " return symbols[\"drop\"]\n", + " case _:\n", + " raise ValueError(f\"{command} does not compute!\")" + ] + }, + { + "cell_type": "markdown", + "id": "441a53f6", + "metadata": {}, + "source": [ + "That `if ` statement (the **guard**) will only let the case block execute if the match is true **and** that `if` expression evaludates to `True`:" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "708c5d16", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " ['move', 'up'] does not compute!\n" + ] + } + ], + "source": [ + "try:\n", + " op([\"move\", \"up\"])\n", + "except Exception as ex:\n", + " print(type(ex), ex)" + ] + }, + { + "cell_type": "markdown", + "id": "1ecf5f83", + "metadata": {}, + "source": [ + "There are many other ways we could have done this - probably better than this, but this was to illustrate how the multiple value matching can work!" + ] + }, + { + "cell_type": "markdown", + "id": "84bec678", + "metadata": {}, + "source": [ + "I urge you to read at least this [tutorial (pep 636)](https://peps.python.org/pep-0636/) on pattern matching." + ] + }, + { + "cell_type": "markdown", + "id": "5b6a7b09", + "metadata": {}, + "source": [ + "#### The `zip` Function" + ] + }, + { + "cell_type": "markdown", + "id": "1294e5f6", + "metadata": {}, + "source": [ + "We use the built-in `zip` function all the time. As we know, the `zip` will stop iterating after the first of the iterables provided in the arguments is exhausted:" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "e33aa60d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[('a', 10), ('b', 20), ('c', 30)]" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "l1 = ['a', 'b', 'c']\n", + "l2 = [10, 20, 30, 40]\n", + "\n", + "list(zip(l1, l2))" + ] + }, + { + "cell_type": "markdown", + "id": "7d310af8", + "metadata": {}, + "source": [ + "As you can see the last element of `l2` is not included in the result since `l1` only had three elements." + ] + }, + { + "cell_type": "markdown", + "id": "a9cf9570", + "metadata": {}, + "source": [ + "We can also use the `zip_longest` function in the `itertools` module to iterate over the longest iterable, providing some default value for any other iterable that has been exhausted:" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "aefa3055", + "metadata": {}, + "outputs": [], + "source": [ + "from itertools import zip_longest" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "ce4779c9", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[('a', 10), ('b', 20), ('c', 30), ('???', 40)]" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list(zip_longest(l1, l2, fillvalue='???'))" + ] + }, + { + "cell_type": "markdown", + "id": "0eb2f702", + "metadata": {}, + "source": [ + "But what if we want to only zip iterables that have the **same** length? We would need to test the length of each iterable first - but if those were iterators instead of iterables, we will have exhausted the iterator, and `zip` would come back \"empty\":" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "3b704410", + "metadata": {}, + "outputs": [], + "source": [ + "l1 = (i ** 2 for i in range(4))\n", + "l2 = (i ** 3 for i in range(3))" + ] + }, + { + "cell_type": "markdown", + "id": "da0d866f", + "metadata": {}, + "source": [ + "We could test to see if `l1` and `l2` are the same length:" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "bc7d34c9", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "len(list(l1)) == len(list(l2))" + ] + }, + { + "cell_type": "markdown", + "id": "0a4a1974", + "metadata": {}, + "source": [ + "But, if we now try to `zip` them:" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "1c3928b9", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[]" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list(zip(l1, l2))" + ] + }, + { + "cell_type": "markdown", + "id": "9ee310e9", + "metadata": {}, + "source": [ + "In Python 3.10, the `zip` function now has a keyword-only parameter called `strict` that will just do the zip, but throw an exception if one of the arguments get exhausted before the others:" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "54722489", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "zip() argument 2 is shorter than argument 1\n" + ] + } + ], + "source": [ + "l1 = (i ** 2 for i in range(4))\n", + "l2 = (i ** 3 for i in range(3))\n", + "\n", + "try:\n", + " list(zip(l1, l2, strict=True))\n", + "except ValueError as ex:\n", + " print(ex)" + ] + }, + { + "cell_type": "markdown", + "id": "2635211a", + "metadata": {}, + "source": [ + "And works just fine if the arguments all have the same length:" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "7fb672cb", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[(0, 0, 0), (1, 1, 1), (4, 8, 16), (9, 27, 81)]" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "l1 = (i ** 2 for i in range(4))\n", + "l2 = (i ** 3 for i in range(4))\n", + "l3 = (i ** 4 for i in range(4))\n", + "\n", + "list(zip(l1, l2, l3))" + ] + }, + { + "cell_type": "markdown", + "id": "501fec3b", + "metadata": {}, + "source": [ + "So why is this useful?" + ] + }, + { + "cell_type": "markdown", + "id": "dab4260e", + "metadata": {}, + "source": [ + "In **many** cases, our code zips iterables that we expect to be of the same length. To avoid bugs in our program, we should check that this condition is true, otherwise zip will silently just zip based on the shortest one. But as we saw with iterators, that can be difficult to do without exhausting the very iterators we are trying to zip. (it can be done, it's just more code)." + ] + }, + { + "cell_type": "markdown", + "id": "c73e5ad1", + "metadata": {}, + "source": [ + "So, if you are one of the lucky devs that gets to write Python 3.10 (or higher :-) ) code, you can just use `strict` whenever you zip things together and expect that they are all of the same length. Much easier to do it this way (and, as we discuss in Exception handling, falls into the category of \"ask forgiveness later\" which we saw was the preferred way (in general) to handle exceptions in our apps, as opposed to the \"look before you leap\" approach we would have to use to test the argument lengths." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "400a56c1", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.10.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From 51a9ec36ad87f56c090dcde0444d9bc453747e58 Mon Sep 17 00:00:00 2001 From: muralidharan <33954528+metafury0-1@users.noreply.github.com> Date: Sun, 3 Apr 2022 17:45:09 +0530 Subject: [PATCH 11/27] Update 12 - Deleting Properties.ipynb typo correction --- Part 4/Section 02 - Classes/12 - Deleting Properties.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Part 4/Section 02 - Classes/12 - Deleting Properties.ipynb b/Part 4/Section 02 - Classes/12 - Deleting Properties.ipynb index c22a97d0..d2875b6f 100644 --- a/Part 4/Section 02 - Classes/12 - Deleting Properties.ipynb +++ b/Part 4/Section 02 - Classes/12 - Deleting Properties.ipynb @@ -13,7 +13,7 @@ "source": [ "Just like we can delete an attribute from an instance object, we can also delete a property from an instance object.\n", "\n", - "Note that this action simply runs the deleter method, but the propertu remains defined **on the class**. It does not remove the property from the class, instead it is generally used to remove the property value from the **instance**." + "Note that this action simply runs the deleter method, but the property remains defined **on the class**. It does not remove the property from the class, instead it is generally used to remove the property value from the **instance**." ] }, { From 0be6fe4562af118a0df0a94033edbca3c0fd6f45 Mon Sep 17 00:00:00 2001 From: muralidharan <33954528+metafury0-1@users.noreply.github.com> Date: Sat, 9 Apr 2022 12:50:45 +0530 Subject: [PATCH 12/27] Update 04 - Strong and Weak References.ipynb typo - memory --- .../04 - Strong and Weak References.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Part 4/Section 08 - Descriptors/04 - Strong and Weak References.ipynb b/Part 4/Section 08 - Descriptors/04 - Strong and Weak References.ipynb index 2335fa2d..cbf470f0 100644 --- a/Part 4/Section 08 - Descriptors/04 - Strong and Weak References.ipynb +++ b/Part 4/Section 08 - Descriptors/04 - Strong and Weak References.ipynb @@ -164,7 +164,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Now our reference count function will not work anymore, since the last reference to the object at that mempry address was removed and that memory address is now meaningless:" + "Now our reference count function will not work anymore, since the last reference to the object at that memory address was removed and that memory address is now meaningless:" ] }, { From 16e2b29061d3ae6e84ce81ce4dbc95b0a18e21ca Mon Sep 17 00:00:00 2001 From: fbaptiste <6551123+fbaptiste@users.noreply.github.com> Date: Sun, 17 Apr 2022 00:10:51 -0700 Subject: [PATCH 13/27] reorganizes folders --- .../01 - Relevant Python 3.10 Changes.ipynb} | 0 .../02 - Relevant Python 3.9 Changes.ipynb | 0 .../03 - Relevant Python 3.8_3.7 Changes.ipynb} | 0 .../04 - Python 3.6 Highlights.ipynb | 0 .../05 - Python 3.6 - Dictionary Ordering.ipynb | 0 ....6 - Preserved Order of kwargs - Named Tuple Application.ipynb | 0 .../07 - Python 3.6 - Underscores and Numeric Literals.ipynb | 0 .../08 - Python 3.6 - f-Strings.ipynb | 0 .../09 - Random Seeds.ipynb | 0 .../10 - Random Choices.ipynb | 0 .../11 - Random Samples.ipynb | 0 .../12 - Timing code using timeit.ipynb | 0 .../13 - Don't Use args and kwargs Names Blindly.ipynb | 0 .../14 -command line arguments/example1.py | 0 .../14 -command line arguments/example10.py | 0 .../14 -command line arguments/example2.py | 0 .../14 -command line arguments/example3.py | 0 .../14 -command line arguments/example4.py | 0 .../14 -command line arguments/example5.py | 0 .../14 -command line arguments/example6.py | 0 .../14 -command line arguments/example7.py | 0 .../14 -command line arguments/example8.py | 0 .../14 -command line arguments/example9.py | 0 .../15 - Sentinel Values for Parameter Defaults.ipynb | 0 .../16 - Simulating a simple Switch in Python.ipynb | 0 .../01 - Relevant Python 3.10 Changes.ipynb} | 0 .../02 - Relevant Python 3.9 Changes.ipynb | 0 .../03 - Relevant Python 3.8_3.7 Changes.ipynb} | 0 .../01 - Relevant Python 3.10 Changes.ipynb} | 0 .../02 - Relevant Python 3.9 Changes.ipynb | 0 .../03 - Relevant Python 3.8_3.7 Changes.ipynb} | 0 .../01 - MappingProxyType.ipynb} | 0 .../01 - Relevant Python 3.10 Changes.ipynb} | 0 .../02 - Relevant Python 3.9 Changes.ipynb | 0 .../03 - Relevant Python 3.8_3.7 Changes.ipynb} | 0 35 files changed, 0 insertions(+), 0 deletions(-) rename Part 1/{Section 10 - Extras/03 - Relevant Python 3.10 Changes.ipynb => Section 10 - Python. Updates/01 - Relevant Python 3.10 Changes.ipynb} (100%) rename Part 1/{Section 10 - Extras => Section 10 - Python. Updates}/02 - Relevant Python 3.9 Changes.ipynb (100%) rename Part 1/{Section 10 - Extras/01 - Relevant Python 3.8 Changes.ipynb => Section 10 - Python. Updates/03 - Relevant Python 3.8_3.7 Changes.ipynb} (100%) rename Part 1/{Section 10 - Extras => Section 10 - Python. Updates}/04 - Python 3.6 Highlights.ipynb (100%) rename Part 1/{Section 10 - Extras => Section 10 - Python. Updates}/05 - Python 3.6 - Dictionary Ordering.ipynb (100%) rename Part 1/{Section 10 - Extras => Section 10 - Python. Updates}/06 - Python 3.6 - Preserved Order of kwargs - Named Tuple Application.ipynb (100%) rename Part 1/{Section 10 - Extras => Section 10 - Python. Updates}/07 - Python 3.6 - Underscores and Numeric Literals.ipynb (100%) rename Part 1/{Section 10 - Extras => Section 10 - Python. Updates}/08 - Python 3.6 - f-Strings.ipynb (100%) rename Part 1/{Section 10 - Extras => Section 11 - Extras}/09 - Random Seeds.ipynb (100%) rename Part 1/{Section 10 - Extras => Section 11 - Extras}/10 - Random Choices.ipynb (100%) rename Part 1/{Section 10 - Extras => Section 11 - Extras}/11 - Random Samples.ipynb (100%) rename Part 1/{Section 10 - Extras => Section 11 - Extras}/12 - Timing code using timeit.ipynb (100%) rename Part 1/{Section 10 - Extras => Section 11 - Extras}/13 - Don't Use args and kwargs Names Blindly.ipynb (100%) rename Part 1/{Section 10 - Extras => Section 11 - Extras}/14 -command line arguments/example1.py (100%) rename Part 1/{Section 10 - Extras => Section 11 - Extras}/14 -command line arguments/example10.py (100%) rename Part 1/{Section 10 - Extras => Section 11 - Extras}/14 -command line arguments/example2.py (100%) rename Part 1/{Section 10 - Extras => Section 11 - Extras}/14 -command line arguments/example3.py (100%) rename Part 1/{Section 10 - Extras => Section 11 - Extras}/14 -command line arguments/example4.py (100%) rename Part 1/{Section 10 - Extras => Section 11 - Extras}/14 -command line arguments/example5.py (100%) rename Part 1/{Section 10 - Extras => Section 11 - Extras}/14 -command line arguments/example6.py (100%) rename Part 1/{Section 10 - Extras => Section 11 - Extras}/14 -command line arguments/example7.py (100%) rename Part 1/{Section 10 - Extras => Section 11 - Extras}/14 -command line arguments/example8.py (100%) rename Part 1/{Section 10 - Extras => Section 11 - Extras}/14 -command line arguments/example9.py (100%) rename Part 1/{Section 10 - Extras => Section 11 - Extras}/15 - Sentinel Values for Parameter Defaults.ipynb (100%) rename Part 1/{Section 10 - Extras => Section 11 - Extras}/16 - Simulating a simple Switch in Python.ipynb (100%) rename Part 2/{Section 14 - Extras/03 - Relevant Python 3.10 Changes.ipynb => Section 14 - Python Updates/01 - Relevant Python 3.10 Changes.ipynb} (100%) rename Part 2/{Section 14 - Extras => Section 14 - Python Updates}/02 - Relevant Python 3.9 Changes.ipynb (100%) rename Part 2/{Section 14 - Extras/01 - Relevant Python 3.8 Changes.ipynb => Section 14 - Python Updates/03 - Relevant Python 3.8_3.7 Changes.ipynb} (100%) rename Part 3/{Section 11 - Extras/03 - Relevant Python 3.10 Changes.ipynb => Section 11 - Python Updates/01 - Relevant Python 3.10 Changes.ipynb} (100%) rename Part 3/{Section 11 - Extras => Section 11 - Python Updates}/02 - Relevant Python 3.9 Changes.ipynb (100%) rename Part 3/{Section 11 - Extras/01 - Relevant Python 3.8 Changes.ipynb => Section 11 - Python Updates/03 - Relevant Python 3.8_3.7 Changes.ipynb} (100%) rename Part 3/{Section 11 - Extras/04 - MappingProxyType.ipynb => Section 12 - Extras/01 - MappingProxyType.ipynb} (100%) rename Part 4/{Section 15 - Extras/03 - Relevant Python 3.10 Changes.ipynb => Section 15 - Python Updates/01 - Relevant Python 3.10 Changes.ipynb} (100%) rename Part 4/{Section 15 - Extras => Section 15 - Python Updates}/02 - Relevant Python 3.9 Changes.ipynb (100%) rename Part 4/{Section 15 - Extras/01 - Relevant Python 3.8 Changes.ipynb => Section 15 - Python Updates/03 - Relevant Python 3.8_3.7 Changes.ipynb} (100%) diff --git a/Part 1/Section 10 - Extras/03 - Relevant Python 3.10 Changes.ipynb b/Part 1/Section 10 - Python. Updates/01 - Relevant Python 3.10 Changes.ipynb similarity index 100% rename from Part 1/Section 10 - Extras/03 - Relevant Python 3.10 Changes.ipynb rename to Part 1/Section 10 - Python. Updates/01 - Relevant Python 3.10 Changes.ipynb diff --git a/Part 1/Section 10 - Extras/02 - Relevant Python 3.9 Changes.ipynb b/Part 1/Section 10 - Python. Updates/02 - Relevant Python 3.9 Changes.ipynb similarity index 100% rename from Part 1/Section 10 - Extras/02 - Relevant Python 3.9 Changes.ipynb rename to Part 1/Section 10 - Python. Updates/02 - Relevant Python 3.9 Changes.ipynb diff --git a/Part 1/Section 10 - Extras/01 - Relevant Python 3.8 Changes.ipynb b/Part 1/Section 10 - Python. Updates/03 - Relevant Python 3.8_3.7 Changes.ipynb similarity index 100% rename from Part 1/Section 10 - Extras/01 - Relevant Python 3.8 Changes.ipynb rename to Part 1/Section 10 - Python. Updates/03 - Relevant Python 3.8_3.7 Changes.ipynb diff --git a/Part 1/Section 10 - Extras/04 - Python 3.6 Highlights.ipynb b/Part 1/Section 10 - Python. Updates/04 - Python 3.6 Highlights.ipynb similarity index 100% rename from Part 1/Section 10 - Extras/04 - Python 3.6 Highlights.ipynb rename to Part 1/Section 10 - Python. Updates/04 - Python 3.6 Highlights.ipynb diff --git a/Part 1/Section 10 - Extras/05 - Python 3.6 - Dictionary Ordering.ipynb b/Part 1/Section 10 - Python. Updates/05 - Python 3.6 - Dictionary Ordering.ipynb similarity index 100% rename from Part 1/Section 10 - Extras/05 - Python 3.6 - Dictionary Ordering.ipynb rename to Part 1/Section 10 - Python. Updates/05 - Python 3.6 - Dictionary Ordering.ipynb diff --git a/Part 1/Section 10 - Extras/06 - Python 3.6 - Preserved Order of kwargs - Named Tuple Application.ipynb b/Part 1/Section 10 - Python. Updates/06 - Python 3.6 - Preserved Order of kwargs - Named Tuple Application.ipynb similarity index 100% rename from Part 1/Section 10 - Extras/06 - Python 3.6 - Preserved Order of kwargs - Named Tuple Application.ipynb rename to Part 1/Section 10 - Python. Updates/06 - Python 3.6 - Preserved Order of kwargs - Named Tuple Application.ipynb diff --git a/Part 1/Section 10 - Extras/07 - Python 3.6 - Underscores and Numeric Literals.ipynb b/Part 1/Section 10 - Python. Updates/07 - Python 3.6 - Underscores and Numeric Literals.ipynb similarity index 100% rename from Part 1/Section 10 - Extras/07 - Python 3.6 - Underscores and Numeric Literals.ipynb rename to Part 1/Section 10 - Python. Updates/07 - Python 3.6 - Underscores and Numeric Literals.ipynb diff --git a/Part 1/Section 10 - Extras/08 - Python 3.6 - f-Strings.ipynb b/Part 1/Section 10 - Python. Updates/08 - Python 3.6 - f-Strings.ipynb similarity index 100% rename from Part 1/Section 10 - Extras/08 - Python 3.6 - f-Strings.ipynb rename to Part 1/Section 10 - Python. Updates/08 - Python 3.6 - f-Strings.ipynb diff --git a/Part 1/Section 10 - Extras/09 - Random Seeds.ipynb b/Part 1/Section 11 - Extras/09 - Random Seeds.ipynb similarity index 100% rename from Part 1/Section 10 - Extras/09 - Random Seeds.ipynb rename to Part 1/Section 11 - Extras/09 - Random Seeds.ipynb diff --git a/Part 1/Section 10 - Extras/10 - Random Choices.ipynb b/Part 1/Section 11 - Extras/10 - Random Choices.ipynb similarity index 100% rename from Part 1/Section 10 - Extras/10 - Random Choices.ipynb rename to Part 1/Section 11 - Extras/10 - Random Choices.ipynb diff --git a/Part 1/Section 10 - Extras/11 - Random Samples.ipynb b/Part 1/Section 11 - Extras/11 - Random Samples.ipynb similarity index 100% rename from Part 1/Section 10 - Extras/11 - Random Samples.ipynb rename to Part 1/Section 11 - Extras/11 - Random Samples.ipynb diff --git a/Part 1/Section 10 - Extras/12 - Timing code using timeit.ipynb b/Part 1/Section 11 - Extras/12 - Timing code using timeit.ipynb similarity index 100% rename from Part 1/Section 10 - Extras/12 - Timing code using timeit.ipynb rename to Part 1/Section 11 - Extras/12 - Timing code using timeit.ipynb diff --git a/Part 1/Section 10 - Extras/13 - Don't Use args and kwargs Names Blindly.ipynb b/Part 1/Section 11 - Extras/13 - Don't Use args and kwargs Names Blindly.ipynb similarity index 100% rename from Part 1/Section 10 - Extras/13 - Don't Use args and kwargs Names Blindly.ipynb rename to Part 1/Section 11 - Extras/13 - Don't Use args and kwargs Names Blindly.ipynb diff --git a/Part 1/Section 10 - Extras/14 -command line arguments/example1.py b/Part 1/Section 11 - Extras/14 -command line arguments/example1.py similarity index 100% rename from Part 1/Section 10 - Extras/14 -command line arguments/example1.py rename to Part 1/Section 11 - Extras/14 -command line arguments/example1.py diff --git a/Part 1/Section 10 - Extras/14 -command line arguments/example10.py b/Part 1/Section 11 - Extras/14 -command line arguments/example10.py similarity index 100% rename from Part 1/Section 10 - Extras/14 -command line arguments/example10.py rename to Part 1/Section 11 - Extras/14 -command line arguments/example10.py diff --git a/Part 1/Section 10 - Extras/14 -command line arguments/example2.py b/Part 1/Section 11 - Extras/14 -command line arguments/example2.py similarity index 100% rename from Part 1/Section 10 - Extras/14 -command line arguments/example2.py rename to Part 1/Section 11 - Extras/14 -command line arguments/example2.py diff --git a/Part 1/Section 10 - Extras/14 -command line arguments/example3.py b/Part 1/Section 11 - Extras/14 -command line arguments/example3.py similarity index 100% rename from Part 1/Section 10 - Extras/14 -command line arguments/example3.py rename to Part 1/Section 11 - Extras/14 -command line arguments/example3.py diff --git a/Part 1/Section 10 - Extras/14 -command line arguments/example4.py b/Part 1/Section 11 - Extras/14 -command line arguments/example4.py similarity index 100% rename from Part 1/Section 10 - Extras/14 -command line arguments/example4.py rename to Part 1/Section 11 - Extras/14 -command line arguments/example4.py diff --git a/Part 1/Section 10 - Extras/14 -command line arguments/example5.py b/Part 1/Section 11 - Extras/14 -command line arguments/example5.py similarity index 100% rename from Part 1/Section 10 - Extras/14 -command line arguments/example5.py rename to Part 1/Section 11 - Extras/14 -command line arguments/example5.py diff --git a/Part 1/Section 10 - Extras/14 -command line arguments/example6.py b/Part 1/Section 11 - Extras/14 -command line arguments/example6.py similarity index 100% rename from Part 1/Section 10 - Extras/14 -command line arguments/example6.py rename to Part 1/Section 11 - Extras/14 -command line arguments/example6.py diff --git a/Part 1/Section 10 - Extras/14 -command line arguments/example7.py b/Part 1/Section 11 - Extras/14 -command line arguments/example7.py similarity index 100% rename from Part 1/Section 10 - Extras/14 -command line arguments/example7.py rename to Part 1/Section 11 - Extras/14 -command line arguments/example7.py diff --git a/Part 1/Section 10 - Extras/14 -command line arguments/example8.py b/Part 1/Section 11 - Extras/14 -command line arguments/example8.py similarity index 100% rename from Part 1/Section 10 - Extras/14 -command line arguments/example8.py rename to Part 1/Section 11 - Extras/14 -command line arguments/example8.py diff --git a/Part 1/Section 10 - Extras/14 -command line arguments/example9.py b/Part 1/Section 11 - Extras/14 -command line arguments/example9.py similarity index 100% rename from Part 1/Section 10 - Extras/14 -command line arguments/example9.py rename to Part 1/Section 11 - Extras/14 -command line arguments/example9.py diff --git a/Part 1/Section 10 - Extras/15 - Sentinel Values for Parameter Defaults.ipynb b/Part 1/Section 11 - Extras/15 - Sentinel Values for Parameter Defaults.ipynb similarity index 100% rename from Part 1/Section 10 - Extras/15 - Sentinel Values for Parameter Defaults.ipynb rename to Part 1/Section 11 - Extras/15 - Sentinel Values for Parameter Defaults.ipynb diff --git a/Part 1/Section 10 - Extras/16 - Simulating a simple Switch in Python.ipynb b/Part 1/Section 11 - Extras/16 - Simulating a simple Switch in Python.ipynb similarity index 100% rename from Part 1/Section 10 - Extras/16 - Simulating a simple Switch in Python.ipynb rename to Part 1/Section 11 - Extras/16 - Simulating a simple Switch in Python.ipynb diff --git a/Part 2/Section 14 - Extras/03 - Relevant Python 3.10 Changes.ipynb b/Part 2/Section 14 - Python Updates/01 - Relevant Python 3.10 Changes.ipynb similarity index 100% rename from Part 2/Section 14 - Extras/03 - Relevant Python 3.10 Changes.ipynb rename to Part 2/Section 14 - Python Updates/01 - Relevant Python 3.10 Changes.ipynb diff --git a/Part 2/Section 14 - Extras/02 - Relevant Python 3.9 Changes.ipynb b/Part 2/Section 14 - Python Updates/02 - Relevant Python 3.9 Changes.ipynb similarity index 100% rename from Part 2/Section 14 - Extras/02 - Relevant Python 3.9 Changes.ipynb rename to Part 2/Section 14 - Python Updates/02 - Relevant Python 3.9 Changes.ipynb diff --git a/Part 2/Section 14 - Extras/01 - Relevant Python 3.8 Changes.ipynb b/Part 2/Section 14 - Python Updates/03 - Relevant Python 3.8_3.7 Changes.ipynb similarity index 100% rename from Part 2/Section 14 - Extras/01 - Relevant Python 3.8 Changes.ipynb rename to Part 2/Section 14 - Python Updates/03 - Relevant Python 3.8_3.7 Changes.ipynb diff --git a/Part 3/Section 11 - Extras/03 - Relevant Python 3.10 Changes.ipynb b/Part 3/Section 11 - Python Updates/01 - Relevant Python 3.10 Changes.ipynb similarity index 100% rename from Part 3/Section 11 - Extras/03 - Relevant Python 3.10 Changes.ipynb rename to Part 3/Section 11 - Python Updates/01 - Relevant Python 3.10 Changes.ipynb diff --git a/Part 3/Section 11 - Extras/02 - Relevant Python 3.9 Changes.ipynb b/Part 3/Section 11 - Python Updates/02 - Relevant Python 3.9 Changes.ipynb similarity index 100% rename from Part 3/Section 11 - Extras/02 - Relevant Python 3.9 Changes.ipynb rename to Part 3/Section 11 - Python Updates/02 - Relevant Python 3.9 Changes.ipynb diff --git a/Part 3/Section 11 - Extras/01 - Relevant Python 3.8 Changes.ipynb b/Part 3/Section 11 - Python Updates/03 - Relevant Python 3.8_3.7 Changes.ipynb similarity index 100% rename from Part 3/Section 11 - Extras/01 - Relevant Python 3.8 Changes.ipynb rename to Part 3/Section 11 - Python Updates/03 - Relevant Python 3.8_3.7 Changes.ipynb diff --git a/Part 3/Section 11 - Extras/04 - MappingProxyType.ipynb b/Part 3/Section 12 - Extras/01 - MappingProxyType.ipynb similarity index 100% rename from Part 3/Section 11 - Extras/04 - MappingProxyType.ipynb rename to Part 3/Section 12 - Extras/01 - MappingProxyType.ipynb diff --git a/Part 4/Section 15 - Extras/03 - Relevant Python 3.10 Changes.ipynb b/Part 4/Section 15 - Python Updates/01 - Relevant Python 3.10 Changes.ipynb similarity index 100% rename from Part 4/Section 15 - Extras/03 - Relevant Python 3.10 Changes.ipynb rename to Part 4/Section 15 - Python Updates/01 - Relevant Python 3.10 Changes.ipynb diff --git a/Part 4/Section 15 - Extras/02 - Relevant Python 3.9 Changes.ipynb b/Part 4/Section 15 - Python Updates/02 - Relevant Python 3.9 Changes.ipynb similarity index 100% rename from Part 4/Section 15 - Extras/02 - Relevant Python 3.9 Changes.ipynb rename to Part 4/Section 15 - Python Updates/02 - Relevant Python 3.9 Changes.ipynb diff --git a/Part 4/Section 15 - Extras/01 - Relevant Python 3.8 Changes.ipynb b/Part 4/Section 15 - Python Updates/03 - Relevant Python 3.8_3.7 Changes.ipynb similarity index 100% rename from Part 4/Section 15 - Extras/01 - Relevant Python 3.8 Changes.ipynb rename to Part 4/Section 15 - Python Updates/03 - Relevant Python 3.8_3.7 Changes.ipynb From aa375f103f486c9c18fc02aa564d984c2c5c9db9 Mon Sep 17 00:00:00 2001 From: fbaptiste <6551123+fbaptiste@users.noreply.github.com> Date: Thu, 21 Apr 2022 22:53:15 -0700 Subject: [PATCH 14/27] walrus operator --- ... Python 3.8 - Assignment Expressions.ipynb | 1082 +++++++++++++++++ ...4 - Relevant Python 3.8_3.7 Changes.ipynb} | 0 ...ipynb => 05 - Python 3.6 Highlights.ipynb} | 0 ... - Python 3.6 - Dictionary Ordering.ipynb} | 0 ...of kwargs - Named Tuple Application.ipynb} | 0 ... - Underscores and Numeric Literals.ipynb} | 0 ...pynb => 09 - Python 3.6 - f-Strings.ipynb} | 0 ... Python 3.8 - Assignment Expressions.ipynb | 1082 +++++++++++++++++ ...4 - Relevant Python 3.8_3.7 Changes.ipynb} | 0 ... Python 3.8 - Assignment Expressions.ipynb | 1082 +++++++++++++++++ ...4 - Relevant Python 3.8_3.7 Changes.ipynb} | 0 ... Python 3.8 - Assignment Expressions.ipynb | 1082 +++++++++++++++++ ...4 - Relevant Python 3.8_3.7 Changes.ipynb} | 0 13 files changed, 4328 insertions(+) create mode 100644 Part 1/Section 10 - Python. Updates/03 - Python 3.8 - Assignment Expressions.ipynb rename Part 1/Section 10 - Python. Updates/{03 - Relevant Python 3.8_3.7 Changes.ipynb => 04 - Relevant Python 3.8_3.7 Changes.ipynb} (100%) rename Part 1/Section 10 - Python. Updates/{04 - Python 3.6 Highlights.ipynb => 05 - Python 3.6 Highlights.ipynb} (100%) rename Part 1/Section 10 - Python. Updates/{05 - Python 3.6 - Dictionary Ordering.ipynb => 06 - Python 3.6 - Dictionary Ordering.ipynb} (100%) rename Part 1/Section 10 - Python. Updates/{06 - Python 3.6 - Preserved Order of kwargs - Named Tuple Application.ipynb => 07 - Python 3.6 - Preserved Order of kwargs - Named Tuple Application.ipynb} (100%) rename Part 1/Section 10 - Python. Updates/{07 - Python 3.6 - Underscores and Numeric Literals.ipynb => 08 - Python 3.6 - Underscores and Numeric Literals.ipynb} (100%) rename Part 1/Section 10 - Python. Updates/{08 - Python 3.6 - f-Strings.ipynb => 09 - Python 3.6 - f-Strings.ipynb} (100%) create mode 100644 Part 2/Section 14 - Python Updates/03 - Python 3.8 - Assignment Expressions.ipynb rename Part 2/Section 14 - Python Updates/{03 - Relevant Python 3.8_3.7 Changes.ipynb => 04 - Relevant Python 3.8_3.7 Changes.ipynb} (100%) create mode 100644 Part 3/Section 11 - Python Updates/03 - Python 3.8 - Assignment Expressions.ipynb rename Part 3/Section 11 - Python Updates/{03 - Relevant Python 3.8_3.7 Changes.ipynb => 04 - Relevant Python 3.8_3.7 Changes.ipynb} (100%) create mode 100644 Part 4/Section 15 - Python Updates/03 - Python 3.8 - Assignment Expressions.ipynb rename Part 4/Section 15 - Python Updates/{03 - Relevant Python 3.8_3.7 Changes.ipynb => 04 - Relevant Python 3.8_3.7 Changes.ipynb} (100%) diff --git a/Part 1/Section 10 - Python. Updates/03 - Python 3.8 - Assignment Expressions.ipynb b/Part 1/Section 10 - Python. Updates/03 - Python 3.8 - Assignment Expressions.ipynb new file mode 100644 index 00000000..53e8b672 --- /dev/null +++ b/Part 1/Section 10 - Python. Updates/03 - Python 3.8 - Assignment Expressions.ipynb @@ -0,0 +1,1082 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "17ad44da", + "metadata": {}, + "source": [ + "### Python 3.8 - Assignment Expressions" + ] + }, + { + "cell_type": "markdown", + "id": "40b0045a", + "metadata": {}, + "source": [ + "Another enhancement to the Python core language that was introduced in Python 3.8 is **assignment expressions**." + ] + }, + { + "cell_type": "markdown", + "id": "cad52d44", + "metadata": {}, + "source": [ + "You can see the pep for it here: [pep 572](https://peps.python.org/pep-0572/)" + ] + }, + { + "cell_type": "markdown", + "id": "285f5b11", + "metadata": {}, + "source": [ + "Remember that an expression is simply a snippet of code that is evaluated.\n", + "\n", + "The following are all examples of expressions:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "357659ec", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1 + 2" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "c7b05302", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[1, 2, 3, 4, 5, 6]" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "[1, 2, 3] + [4, 5, 6]" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "a68603b6", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'PYTHON'" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\" python \".lstrip().rstrip().upper()" + ] + }, + { + "cell_type": "markdown", + "id": "201b3156", + "metadata": {}, + "source": [ + "And an assignment is simply when we assign an expression result to a variable:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "b08e7b15", + "metadata": {}, + "outputs": [], + "source": [ + "a = 1 + 2\n", + "b = [1, 2, 3] + [4, 5, 6]\n", + "c = \" python \".lstrip().rstrip().upper()" + ] + }, + { + "cell_type": "markdown", + "id": "54232d7a", + "metadata": {}, + "source": [ + "As you can see, we have to different steps here.\n", + "\n", + "We assign the result of an expression (the right hand side) to a variable (the left hand side) using the equals (`=`) sign." + ] + }, + { + "cell_type": "markdown", + "id": "e0ca1920", + "metadata": {}, + "source": [ + "So we have two **distinct** (totally separate) aspects here - the expression, and the assignment." + ] + }, + { + "cell_type": "markdown", + "id": "90b2d0ab", + "metadata": {}, + "source": [ + "So what are **expression assignments**?" + ] + }, + { + "cell_type": "markdown", + "id": "d2065f05", + "metadata": {}, + "source": [ + "Expression assignments allows us to assign expressions to a variable **inside** an expression, using the `:=` operator (the so-called *walrus* operator)" + ] + }, + { + "cell_type": "markdown", + "id": "0c400c75", + "metadata": {}, + "source": [ + "Confusing? :-)" + ] + }, + { + "cell_type": "markdown", + "id": "084caec6", + "metadata": {}, + "source": [ + "Let's take a look at a very simple example first:" + ] + }, + { + "cell_type": "markdown", + "id": "985d592b", + "metadata": {}, + "source": [ + "Starting with an expression:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "c9b56fb0", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1 + 2" + ] + }, + { + "cell_type": "markdown", + "id": "b89a8762", + "metadata": {}, + "source": [ + "We could assign the result of that expression to some variable:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "225ca5f8", + "metadata": {}, + "outputs": [], + "source": [ + "a = 1 + 2" + ] + }, + { + "cell_type": "markdown", + "id": "1513437a", + "metadata": {}, + "source": [ + "But, we could also write the expression and assignment this way (not the parentheses that enclose the expression):" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "572aaf6a", + "metadata": {}, + "outputs": [], + "source": [ + "a = (1 + 2)" + ] + }, + { + "cell_type": "markdown", + "id": "8d0e2885", + "metadata": {}, + "source": [ + "With the expression assignment operator, we could actually assign the result of that expression inside the expression itself:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "0fa9c199", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(x := 1 + 2)" + ] + }, + { + "cell_type": "markdown", + "id": "1eafbee8", + "metadata": {}, + "source": [ + "As you can see, the expression returned a result (`3`), but it also assigned that result to the variable `x`:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "915d5b48", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "x" + ] + }, + { + "cell_type": "markdown", + "id": "e35a8501", + "metadata": {}, + "source": [ + "Note that the parentheses in this case are **necessary** - simply writing this would not work:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "890070de", + "metadata": {}, + "outputs": [ + { + "ename": "SyntaxError", + "evalue": "invalid syntax (746607632.py, line 1)", + "output_type": "error", + "traceback": [ + "\u001b[0;36m Input \u001b[0;32mIn [10]\u001b[0;36m\u001b[0m\n\u001b[0;31m x := 1 + 2\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n" + ] + } + ], + "source": [ + "x := 1 + 2" + ] + }, + { + "cell_type": "markdown", + "id": "d0b5f281", + "metadata": {}, + "source": [ + "This is because the `:=` operator must be used inside an **expression**, so we can force it by using the parentheses." + ] + }, + { + "cell_type": "markdown", + "id": "3195a0a8", + "metadata": {}, + "source": [ + "We could even do this:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "6d1e232c", + "metadata": {}, + "outputs": [], + "source": [ + "a = (x := 10 + 20)" + ] + }, + { + "cell_type": "markdown", + "id": "35a7ed36", + "metadata": {}, + "source": [ + "Then, `a`, and `x` are:" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "96bf1532", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(30, 30)" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a, x" + ] + }, + { + "cell_type": "markdown", + "id": "638c195a", + "metadata": {}, + "source": [ + "Yeah, even more confusing! But in a minute I'll show you why this can be very useful." + ] + }, + { + "cell_type": "markdown", + "id": "4a625536", + "metadata": {}, + "source": [ + "Before we move on to that, let's see how this assignment expression works when we deal with mutable objects such as lists:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "a2ee53ae", + "metadata": {}, + "outputs": [], + "source": [ + "l1 = (l2 := [1, 2] + [3, 4])" + ] + }, + { + "cell_type": "markdown", + "id": "6e58d69f", + "metadata": {}, + "source": [ + "Here, `l1` was the result of the concatenation of the two lists:" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "2d3ae1a4", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "([1, 2, 3, 4], 4393044992)" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "l1, id(l1)" + ] + }, + { + "cell_type": "markdown", + "id": "88aee44a", + "metadata": {}, + "source": [ + "But what about `l2`? It should be a list with the same values, but is it the same object reference as `l1`?" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "fc8fe138", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "([1, 2, 3, 4], 4393044992)" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "l2, id(l2)" + ] + }, + { + "cell_type": "markdown", + "id": "243f0d28", + "metadata": {}, + "source": [ + "And indeed, they are not only the same values, but the same object." + ] + }, + { + "cell_type": "markdown", + "id": "e7268ec5", + "metadata": {}, + "source": [ + "Usually this is not an issue, but keep it in mind because you end up with shared references that you may not realize exist." + ] + }, + { + "cell_type": "markdown", + "id": "e27c849e", + "metadata": {}, + "source": [ + "So now, why is this useful?" + ] + }, + { + "cell_type": "markdown", + "id": "a57d9679", + "metadata": {}, + "source": [ + "Often, we end up writing expressions in terms of other sub expressions, not just for clarity, but sometimes to **avoid repeating** function calls or expression evaluations." + ] + }, + { + "cell_type": "markdown", + "id": "9a686538", + "metadata": {}, + "source": [ + "#### Example 1" + ] + }, + { + "cell_type": "markdown", + "id": "3131d1f1", + "metadata": {}, + "source": [ + "Suppose we have some long running function:" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "f66dd3b5", + "metadata": {}, + "outputs": [], + "source": [ + "import time\n", + "import math\n", + "\n", + "def slow_function(x, y):\n", + " time.sleep(0.5)\n", + " return round(math.sqrt(x**2 + y**2))" + ] + }, + { + "cell_type": "markdown", + "id": "9ddce261", + "metadata": {}, + "source": [ + "Now executing this function will take about 2 seconds to run every time it is called, even when calling it with the same values (we could of course use some LRU caching, but only if the function is **deterministic** - if the function is reading data from a web site, or a database, the result for the same arguments may not be the same and so LRU caching is not even a viable option in this case).\n", + "\n", + "> A **deterministic** function is a function that for the **same** inputs, always returns the **same** result:\n", + ">\n", + "> Obviously the function we have above is deterministic, but this one would not be:\n", + ">```\n", + ">def get_price(symbol):\n", + "> # query an API for latest price for symbol\n", + "> price = ...\n", + "> return price\n", + ">``` \n", + "> A function that returns the current date or time, or a random number, etc are all non-deterministic functions." + ] + }, + { + "cell_type": "markdown", + "id": "6e503d9b", + "metadata": {}, + "source": [ + "So, LRU caching is not always an option." + ] + }, + { + "cell_type": "markdown", + "id": "05027bcc", + "metadata": {}, + "source": [ + "Let's see an example of why we would want to assign the result of our long running function to a variable, instead of just using it directly." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "a9babf8f", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0, 4, 6, 8, 10]\n", + "Elapsed: 7.6 seconds\n" + ] + } + ], + "source": [ + "from time import perf_counter\n", + "\n", + "start = perf_counter()\n", + "even_results = []\n", + "for i in range(10):\n", + " if slow_function(i, i) % 2 == 0:\n", + " even_results.append(slow_function(i, i))\n", + "end = perf_counter()\n", + "print(even_results)\n", + "print(f'Elapsed: {end - start:.1f} seconds')" + ] + }, + { + "cell_type": "markdown", + "id": "3bdd4e15", + "metadata": {}, + "source": [ + "Well that was painfully slow!\n", + "\n", + "But notice that we are calling the same function, with the same arguments twice - we can eliminate that!" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "19453896", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0, 4, 6, 8, 10]\n", + "Elapsed: 5.0 seconds\n" + ] + } + ], + "source": [ + "start = perf_counter()\n", + "even_results = []\n", + "for i in range(10):\n", + " result = slow_function(i, i)\n", + " if result % 2 == 0:\n", + " even_results.append(result)\n", + "end = perf_counter()\n", + "print(even_results)\n", + "print(f'Elapsed: {end - start:.1f} seconds')" + ] + }, + { + "cell_type": "markdown", + "id": "3b6091cb", + "metadata": {}, + "source": [ + "So we are able to speed this code up, by using that interim `result` variable - also note how `result` is basically a throw away variable (we typically would not use such a variable outside the loop itself - exceptions happen of course)." + ] + }, + { + "cell_type": "markdown", + "id": "8acf631f", + "metadata": {}, + "source": [ + "But notice something about that code? It's ugly looking - we are building up a list by running through a loop and adding to an initially empty list, one element at a time." + ] + }, + { + "cell_type": "markdown", + "id": "6293f28c", + "metadata": {}, + "source": [ + "We can do better! List comprehensions of course." + ] + }, + { + "cell_type": "markdown", + "id": "9cdff5a7", + "metadata": {}, + "source": [ + "But... we can't write that `result = slow_function(i, i)` in our list comprehension - so we would be back to the original (and slower) may of doing it:" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "0d89b92d", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0, 4, 6, 8, 10]\n", + "Elapsed: 7.6 seconds\n" + ] + } + ], + "source": [ + "start = perf_counter()\n", + "even_results = [\n", + " slow_function(i, i)\n", + " for i in range(10)\n", + " if slow_function(i, i) % 2 == 0\n", + "]\n", + "end = perf_counter()\n", + "print(even_results)\n", + "print(f'Elapsed: {end - start:.1f} seconds')" + ] + }, + { + "cell_type": "markdown", + "id": "8db81b84", + "metadata": {}, + "source": [ + ":-(" + ] + }, + { + "cell_type": "markdown", + "id": "6c42d90a", + "metadata": {}, + "source": [ + "And this is where the assignment expression operator comes in very handy:" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "eefb7ce7", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0, 4, 6, 8, 10]\n", + "Elapsed: 5.0 seconds\n" + ] + } + ], + "source": [ + "start = perf_counter()\n", + "even_results = [\n", + " result\n", + " for i in range(10)\n", + " if (result := slow_function(i, i)) % 2 == 0\n", + "]\n", + "end = perf_counter()\n", + "print(even_results)\n", + "print(f'Elapsed: {end - start:.1f} seconds')" + ] + }, + { + "cell_type": "markdown", + "id": "c1c77b0c", + "metadata": {}, + "source": [ + "Notice how using the `:=` operator, we assign the result of `slow_function(i, i)` to `result` as part of the expression itself, and then re-use that computed value for the elements of the list." + ] + }, + { + "cell_type": "markdown", + "id": "a9e3744d", + "metadata": {}, + "source": [ + "You may be asking yourself, why not write it this way:" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "db2bc22b", + "metadata": {}, + "outputs": [], + "source": [ + "del result" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "cd112bb8", + "metadata": {}, + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'result' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", + "Input \u001b[0;32mIn [22]\u001b[0m, in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0m even_results \u001b[38;5;241m=\u001b[39m [\n\u001b[1;32m 2\u001b[0m (result \u001b[38;5;241m:=\u001b[39m slow_function(i, i))\n\u001b[1;32m 3\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(\u001b[38;5;241m10\u001b[39m)\n\u001b[1;32m 4\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m result \u001b[38;5;241m%\u001b[39m \u001b[38;5;241m2\u001b[39m \u001b[38;5;241m==\u001b[39m \u001b[38;5;241m0\u001b[39m\n\u001b[1;32m 5\u001b[0m ]\n", + "Input \u001b[0;32mIn [22]\u001b[0m, in \u001b[0;36m\u001b[0;34m(.0)\u001b[0m\n\u001b[1;32m 1\u001b[0m even_results \u001b[38;5;241m=\u001b[39m [\n\u001b[1;32m 2\u001b[0m (result \u001b[38;5;241m:=\u001b[39m slow_function(i, i))\n\u001b[1;32m 3\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(\u001b[38;5;241m10\u001b[39m)\n\u001b[0;32m----> 4\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[43mresult\u001b[49m \u001b[38;5;241m%\u001b[39m \u001b[38;5;241m2\u001b[39m \u001b[38;5;241m==\u001b[39m \u001b[38;5;241m0\u001b[39m\n\u001b[1;32m 5\u001b[0m ]\n", + "\u001b[0;31mNameError\u001b[0m: name 'result' is not defined" + ] + } + ], + "source": [ + "even_results = [\n", + " (result := slow_function(i, i))\n", + " for i in range(10)\n", + " if result % 2 == 0\n", + "]" + ] + }, + { + "cell_type": "markdown", + "id": "5c0229a0", + "metadata": {}, + "source": [ + "This happens because in a list comprehension, the loop starts running, then the `if` clause (if any) is evaluated, and then the element expression is evaluated - hence why we place the assignment expression in the `if`." + ] + }, + { + "cell_type": "markdown", + "id": "cc652ad3", + "metadata": {}, + "source": [ + "**Example 2**" + ] + }, + { + "cell_type": "markdown", + "id": "093978ca", + "metadata": {}, + "source": [ + "Here's another scenario where this new operator could be quite useful." + ] + }, + { + "cell_type": "markdown", + "id": "edbdc1b7", + "metadata": {}, + "source": [ + "You want to return the result of an expression but only if it satisfies some criteria.\n", + "\n", + "For example, let's say we write a generator function to produce n even random integers between 1 and 10:" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "a75a9138", + "metadata": {}, + "outputs": [], + "source": [ + "import random\n", + "\n", + "random.seed(0)\n", + "def even_random(n):\n", + " cnt = 0\n", + " while cnt <= n:\n", + " cnt += 1\n", + " number = random.randint(0, 10)\n", + " if number % 2 == 0:\n", + " yield number" + ] + }, + { + "cell_type": "markdown", + "id": "9f19d723", + "metadata": {}, + "source": [ + "We can then call the generator function:" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "53f19b5e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[6, 6, 0, 4, 8]" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list(even_random(5))" + ] + }, + { + "cell_type": "markdown", + "id": "76ac9cf0", + "metadata": {}, + "source": [ + "We can make our code a little more concise without losing readability by using the `:=` operator:" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "b849d145", + "metadata": {}, + "outputs": [], + "source": [ + "random.seed(0)\n", + "def even_random(n):\n", + " cnt = 0\n", + " while (cnt := cnt + 1) <= n:\n", + " if (number := random.randint(0, 10)) % 2 == 0:\n", + " yield number" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "b847739e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[6, 6, 0, 4, 8]" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list(even_random(5))" + ] + }, + { + "cell_type": "markdown", + "id": "5621f621", + "metadata": {}, + "source": [ + "#### Example 3" + ] + }, + { + "cell_type": "markdown", + "id": "9e1719fd", + "metadata": {}, + "source": [ + "Here's another example where we are consuming some generator, until some condition is met." + ] + }, + { + "cell_type": "markdown", + "id": "9822e4d5", + "metadata": {}, + "source": [ + "Let's write a generator function:" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "623069a1", + "metadata": {}, + "outputs": [], + "source": [ + "def gen():\n", + " while True:\n", + " yield list(range(random.randint(0, 10)))" + ] + }, + { + "cell_type": "markdown", + "id": "da617a93", + "metadata": {}, + "source": [ + "And let's print out a frew values from this generator:" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "56b83447", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0, 1, 2]\n", + "[0, 1, 2, 3, 4]\n", + "[0, 1, 2, 3, 4, 5]\n", + "[0, 1]\n", + "[0, 1, 2]\n", + "[]\n", + "[0]\n", + "[0, 1]\n", + "[0, 1, 2]\n", + "[0, 1, 2, 3, 4, 5, 6, 7]\n" + ] + } + ], + "source": [ + "random.seed(8)\n", + "my_gen = gen()\n", + "for _ in range(10):\n", + " print(next(my_gen))" + ] + }, + { + "cell_type": "markdown", + "id": "ad1bb10e", + "metadata": {}, + "source": [ + "You'll notice that the fourth element is a list with 2 values.\n", + "\n", + "What we want to do now, is process the lists yielded by the generator, until we hit a list with two values, at which point we want to stop processing it." + ] + }, + { + "cell_type": "markdown", + "id": "25d4dab3", + "metadata": {}, + "source": [ + "We could do it this way with a `while` loop:" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "id": "f5a068ab", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0, 1, 2]\n", + "[0, 1, 2, 3, 4]\n", + "[0, 1, 2, 3, 4, 5]\n" + ] + } + ], + "source": [ + "random.seed(8)\n", + "my_gen = gen()\n", + "while True:\n", + " l = next(my_gen)\n", + " if len(l) <= 2:\n", + " break\n", + " print(l)" + ] + }, + { + "cell_type": "markdown", + "id": "fcf1f1a2", + "metadata": {}, + "source": [ + "Instead, we could re-write this way using the `:=` operator:" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "id": "7568cf9c", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0, 1, 2]\n", + "[0, 1, 2, 3, 4]\n", + "[0, 1, 2, 3, 4, 5]\n" + ] + } + ], + "source": [ + "random.seed(8)\n", + "my_gen = gen()\n", + "while len(l := next(my_gen)) > 2:\n", + " print(l)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d9f1b587", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.10.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Part 1/Section 10 - Python. Updates/03 - Relevant Python 3.8_3.7 Changes.ipynb b/Part 1/Section 10 - Python. Updates/04 - Relevant Python 3.8_3.7 Changes.ipynb similarity index 100% rename from Part 1/Section 10 - Python. Updates/03 - Relevant Python 3.8_3.7 Changes.ipynb rename to Part 1/Section 10 - Python. Updates/04 - Relevant Python 3.8_3.7 Changes.ipynb diff --git a/Part 1/Section 10 - Python. Updates/04 - Python 3.6 Highlights.ipynb b/Part 1/Section 10 - Python. Updates/05 - Python 3.6 Highlights.ipynb similarity index 100% rename from Part 1/Section 10 - Python. Updates/04 - Python 3.6 Highlights.ipynb rename to Part 1/Section 10 - Python. Updates/05 - Python 3.6 Highlights.ipynb diff --git a/Part 1/Section 10 - Python. Updates/05 - Python 3.6 - Dictionary Ordering.ipynb b/Part 1/Section 10 - Python. Updates/06 - Python 3.6 - Dictionary Ordering.ipynb similarity index 100% rename from Part 1/Section 10 - Python. Updates/05 - Python 3.6 - Dictionary Ordering.ipynb rename to Part 1/Section 10 - Python. Updates/06 - Python 3.6 - Dictionary Ordering.ipynb diff --git a/Part 1/Section 10 - Python. Updates/06 - Python 3.6 - Preserved Order of kwargs - Named Tuple Application.ipynb b/Part 1/Section 10 - Python. Updates/07 - Python 3.6 - Preserved Order of kwargs - Named Tuple Application.ipynb similarity index 100% rename from Part 1/Section 10 - Python. Updates/06 - Python 3.6 - Preserved Order of kwargs - Named Tuple Application.ipynb rename to Part 1/Section 10 - Python. Updates/07 - Python 3.6 - Preserved Order of kwargs - Named Tuple Application.ipynb diff --git a/Part 1/Section 10 - Python. Updates/07 - Python 3.6 - Underscores and Numeric Literals.ipynb b/Part 1/Section 10 - Python. Updates/08 - Python 3.6 - Underscores and Numeric Literals.ipynb similarity index 100% rename from Part 1/Section 10 - Python. Updates/07 - Python 3.6 - Underscores and Numeric Literals.ipynb rename to Part 1/Section 10 - Python. Updates/08 - Python 3.6 - Underscores and Numeric Literals.ipynb diff --git a/Part 1/Section 10 - Python. Updates/08 - Python 3.6 - f-Strings.ipynb b/Part 1/Section 10 - Python. Updates/09 - Python 3.6 - f-Strings.ipynb similarity index 100% rename from Part 1/Section 10 - Python. Updates/08 - Python 3.6 - f-Strings.ipynb rename to Part 1/Section 10 - Python. Updates/09 - Python 3.6 - f-Strings.ipynb diff --git a/Part 2/Section 14 - Python Updates/03 - Python 3.8 - Assignment Expressions.ipynb b/Part 2/Section 14 - Python Updates/03 - Python 3.8 - Assignment Expressions.ipynb new file mode 100644 index 00000000..53e8b672 --- /dev/null +++ b/Part 2/Section 14 - Python Updates/03 - Python 3.8 - Assignment Expressions.ipynb @@ -0,0 +1,1082 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "17ad44da", + "metadata": {}, + "source": [ + "### Python 3.8 - Assignment Expressions" + ] + }, + { + "cell_type": "markdown", + "id": "40b0045a", + "metadata": {}, + "source": [ + "Another enhancement to the Python core language that was introduced in Python 3.8 is **assignment expressions**." + ] + }, + { + "cell_type": "markdown", + "id": "cad52d44", + "metadata": {}, + "source": [ + "You can see the pep for it here: [pep 572](https://peps.python.org/pep-0572/)" + ] + }, + { + "cell_type": "markdown", + "id": "285f5b11", + "metadata": {}, + "source": [ + "Remember that an expression is simply a snippet of code that is evaluated.\n", + "\n", + "The following are all examples of expressions:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "357659ec", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1 + 2" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "c7b05302", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[1, 2, 3, 4, 5, 6]" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "[1, 2, 3] + [4, 5, 6]" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "a68603b6", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'PYTHON'" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\" python \".lstrip().rstrip().upper()" + ] + }, + { + "cell_type": "markdown", + "id": "201b3156", + "metadata": {}, + "source": [ + "And an assignment is simply when we assign an expression result to a variable:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "b08e7b15", + "metadata": {}, + "outputs": [], + "source": [ + "a = 1 + 2\n", + "b = [1, 2, 3] + [4, 5, 6]\n", + "c = \" python \".lstrip().rstrip().upper()" + ] + }, + { + "cell_type": "markdown", + "id": "54232d7a", + "metadata": {}, + "source": [ + "As you can see, we have to different steps here.\n", + "\n", + "We assign the result of an expression (the right hand side) to a variable (the left hand side) using the equals (`=`) sign." + ] + }, + { + "cell_type": "markdown", + "id": "e0ca1920", + "metadata": {}, + "source": [ + "So we have two **distinct** (totally separate) aspects here - the expression, and the assignment." + ] + }, + { + "cell_type": "markdown", + "id": "90b2d0ab", + "metadata": {}, + "source": [ + "So what are **expression assignments**?" + ] + }, + { + "cell_type": "markdown", + "id": "d2065f05", + "metadata": {}, + "source": [ + "Expression assignments allows us to assign expressions to a variable **inside** an expression, using the `:=` operator (the so-called *walrus* operator)" + ] + }, + { + "cell_type": "markdown", + "id": "0c400c75", + "metadata": {}, + "source": [ + "Confusing? :-)" + ] + }, + { + "cell_type": "markdown", + "id": "084caec6", + "metadata": {}, + "source": [ + "Let's take a look at a very simple example first:" + ] + }, + { + "cell_type": "markdown", + "id": "985d592b", + "metadata": {}, + "source": [ + "Starting with an expression:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "c9b56fb0", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1 + 2" + ] + }, + { + "cell_type": "markdown", + "id": "b89a8762", + "metadata": {}, + "source": [ + "We could assign the result of that expression to some variable:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "225ca5f8", + "metadata": {}, + "outputs": [], + "source": [ + "a = 1 + 2" + ] + }, + { + "cell_type": "markdown", + "id": "1513437a", + "metadata": {}, + "source": [ + "But, we could also write the expression and assignment this way (not the parentheses that enclose the expression):" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "572aaf6a", + "metadata": {}, + "outputs": [], + "source": [ + "a = (1 + 2)" + ] + }, + { + "cell_type": "markdown", + "id": "8d0e2885", + "metadata": {}, + "source": [ + "With the expression assignment operator, we could actually assign the result of that expression inside the expression itself:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "0fa9c199", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(x := 1 + 2)" + ] + }, + { + "cell_type": "markdown", + "id": "1eafbee8", + "metadata": {}, + "source": [ + "As you can see, the expression returned a result (`3`), but it also assigned that result to the variable `x`:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "915d5b48", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "x" + ] + }, + { + "cell_type": "markdown", + "id": "e35a8501", + "metadata": {}, + "source": [ + "Note that the parentheses in this case are **necessary** - simply writing this would not work:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "890070de", + "metadata": {}, + "outputs": [ + { + "ename": "SyntaxError", + "evalue": "invalid syntax (746607632.py, line 1)", + "output_type": "error", + "traceback": [ + "\u001b[0;36m Input \u001b[0;32mIn [10]\u001b[0;36m\u001b[0m\n\u001b[0;31m x := 1 + 2\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n" + ] + } + ], + "source": [ + "x := 1 + 2" + ] + }, + { + "cell_type": "markdown", + "id": "d0b5f281", + "metadata": {}, + "source": [ + "This is because the `:=` operator must be used inside an **expression**, so we can force it by using the parentheses." + ] + }, + { + "cell_type": "markdown", + "id": "3195a0a8", + "metadata": {}, + "source": [ + "We could even do this:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "6d1e232c", + "metadata": {}, + "outputs": [], + "source": [ + "a = (x := 10 + 20)" + ] + }, + { + "cell_type": "markdown", + "id": "35a7ed36", + "metadata": {}, + "source": [ + "Then, `a`, and `x` are:" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "96bf1532", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(30, 30)" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a, x" + ] + }, + { + "cell_type": "markdown", + "id": "638c195a", + "metadata": {}, + "source": [ + "Yeah, even more confusing! But in a minute I'll show you why this can be very useful." + ] + }, + { + "cell_type": "markdown", + "id": "4a625536", + "metadata": {}, + "source": [ + "Before we move on to that, let's see how this assignment expression works when we deal with mutable objects such as lists:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "a2ee53ae", + "metadata": {}, + "outputs": [], + "source": [ + "l1 = (l2 := [1, 2] + [3, 4])" + ] + }, + { + "cell_type": "markdown", + "id": "6e58d69f", + "metadata": {}, + "source": [ + "Here, `l1` was the result of the concatenation of the two lists:" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "2d3ae1a4", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "([1, 2, 3, 4], 4393044992)" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "l1, id(l1)" + ] + }, + { + "cell_type": "markdown", + "id": "88aee44a", + "metadata": {}, + "source": [ + "But what about `l2`? It should be a list with the same values, but is it the same object reference as `l1`?" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "fc8fe138", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "([1, 2, 3, 4], 4393044992)" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "l2, id(l2)" + ] + }, + { + "cell_type": "markdown", + "id": "243f0d28", + "metadata": {}, + "source": [ + "And indeed, they are not only the same values, but the same object." + ] + }, + { + "cell_type": "markdown", + "id": "e7268ec5", + "metadata": {}, + "source": [ + "Usually this is not an issue, but keep it in mind because you end up with shared references that you may not realize exist." + ] + }, + { + "cell_type": "markdown", + "id": "e27c849e", + "metadata": {}, + "source": [ + "So now, why is this useful?" + ] + }, + { + "cell_type": "markdown", + "id": "a57d9679", + "metadata": {}, + "source": [ + "Often, we end up writing expressions in terms of other sub expressions, not just for clarity, but sometimes to **avoid repeating** function calls or expression evaluations." + ] + }, + { + "cell_type": "markdown", + "id": "9a686538", + "metadata": {}, + "source": [ + "#### Example 1" + ] + }, + { + "cell_type": "markdown", + "id": "3131d1f1", + "metadata": {}, + "source": [ + "Suppose we have some long running function:" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "f66dd3b5", + "metadata": {}, + "outputs": [], + "source": [ + "import time\n", + "import math\n", + "\n", + "def slow_function(x, y):\n", + " time.sleep(0.5)\n", + " return round(math.sqrt(x**2 + y**2))" + ] + }, + { + "cell_type": "markdown", + "id": "9ddce261", + "metadata": {}, + "source": [ + "Now executing this function will take about 2 seconds to run every time it is called, even when calling it with the same values (we could of course use some LRU caching, but only if the function is **deterministic** - if the function is reading data from a web site, or a database, the result for the same arguments may not be the same and so LRU caching is not even a viable option in this case).\n", + "\n", + "> A **deterministic** function is a function that for the **same** inputs, always returns the **same** result:\n", + ">\n", + "> Obviously the function we have above is deterministic, but this one would not be:\n", + ">```\n", + ">def get_price(symbol):\n", + "> # query an API for latest price for symbol\n", + "> price = ...\n", + "> return price\n", + ">``` \n", + "> A function that returns the current date or time, or a random number, etc are all non-deterministic functions." + ] + }, + { + "cell_type": "markdown", + "id": "6e503d9b", + "metadata": {}, + "source": [ + "So, LRU caching is not always an option." + ] + }, + { + "cell_type": "markdown", + "id": "05027bcc", + "metadata": {}, + "source": [ + "Let's see an example of why we would want to assign the result of our long running function to a variable, instead of just using it directly." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "a9babf8f", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0, 4, 6, 8, 10]\n", + "Elapsed: 7.6 seconds\n" + ] + } + ], + "source": [ + "from time import perf_counter\n", + "\n", + "start = perf_counter()\n", + "even_results = []\n", + "for i in range(10):\n", + " if slow_function(i, i) % 2 == 0:\n", + " even_results.append(slow_function(i, i))\n", + "end = perf_counter()\n", + "print(even_results)\n", + "print(f'Elapsed: {end - start:.1f} seconds')" + ] + }, + { + "cell_type": "markdown", + "id": "3bdd4e15", + "metadata": {}, + "source": [ + "Well that was painfully slow!\n", + "\n", + "But notice that we are calling the same function, with the same arguments twice - we can eliminate that!" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "19453896", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0, 4, 6, 8, 10]\n", + "Elapsed: 5.0 seconds\n" + ] + } + ], + "source": [ + "start = perf_counter()\n", + "even_results = []\n", + "for i in range(10):\n", + " result = slow_function(i, i)\n", + " if result % 2 == 0:\n", + " even_results.append(result)\n", + "end = perf_counter()\n", + "print(even_results)\n", + "print(f'Elapsed: {end - start:.1f} seconds')" + ] + }, + { + "cell_type": "markdown", + "id": "3b6091cb", + "metadata": {}, + "source": [ + "So we are able to speed this code up, by using that interim `result` variable - also note how `result` is basically a throw away variable (we typically would not use such a variable outside the loop itself - exceptions happen of course)." + ] + }, + { + "cell_type": "markdown", + "id": "8acf631f", + "metadata": {}, + "source": [ + "But notice something about that code? It's ugly looking - we are building up a list by running through a loop and adding to an initially empty list, one element at a time." + ] + }, + { + "cell_type": "markdown", + "id": "6293f28c", + "metadata": {}, + "source": [ + "We can do better! List comprehensions of course." + ] + }, + { + "cell_type": "markdown", + "id": "9cdff5a7", + "metadata": {}, + "source": [ + "But... we can't write that `result = slow_function(i, i)` in our list comprehension - so we would be back to the original (and slower) may of doing it:" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "0d89b92d", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0, 4, 6, 8, 10]\n", + "Elapsed: 7.6 seconds\n" + ] + } + ], + "source": [ + "start = perf_counter()\n", + "even_results = [\n", + " slow_function(i, i)\n", + " for i in range(10)\n", + " if slow_function(i, i) % 2 == 0\n", + "]\n", + "end = perf_counter()\n", + "print(even_results)\n", + "print(f'Elapsed: {end - start:.1f} seconds')" + ] + }, + { + "cell_type": "markdown", + "id": "8db81b84", + "metadata": {}, + "source": [ + ":-(" + ] + }, + { + "cell_type": "markdown", + "id": "6c42d90a", + "metadata": {}, + "source": [ + "And this is where the assignment expression operator comes in very handy:" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "eefb7ce7", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0, 4, 6, 8, 10]\n", + "Elapsed: 5.0 seconds\n" + ] + } + ], + "source": [ + "start = perf_counter()\n", + "even_results = [\n", + " result\n", + " for i in range(10)\n", + " if (result := slow_function(i, i)) % 2 == 0\n", + "]\n", + "end = perf_counter()\n", + "print(even_results)\n", + "print(f'Elapsed: {end - start:.1f} seconds')" + ] + }, + { + "cell_type": "markdown", + "id": "c1c77b0c", + "metadata": {}, + "source": [ + "Notice how using the `:=` operator, we assign the result of `slow_function(i, i)` to `result` as part of the expression itself, and then re-use that computed value for the elements of the list." + ] + }, + { + "cell_type": "markdown", + "id": "a9e3744d", + "metadata": {}, + "source": [ + "You may be asking yourself, why not write it this way:" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "db2bc22b", + "metadata": {}, + "outputs": [], + "source": [ + "del result" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "cd112bb8", + "metadata": {}, + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'result' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", + "Input \u001b[0;32mIn [22]\u001b[0m, in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0m even_results \u001b[38;5;241m=\u001b[39m [\n\u001b[1;32m 2\u001b[0m (result \u001b[38;5;241m:=\u001b[39m slow_function(i, i))\n\u001b[1;32m 3\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(\u001b[38;5;241m10\u001b[39m)\n\u001b[1;32m 4\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m result \u001b[38;5;241m%\u001b[39m \u001b[38;5;241m2\u001b[39m \u001b[38;5;241m==\u001b[39m \u001b[38;5;241m0\u001b[39m\n\u001b[1;32m 5\u001b[0m ]\n", + "Input \u001b[0;32mIn [22]\u001b[0m, in \u001b[0;36m\u001b[0;34m(.0)\u001b[0m\n\u001b[1;32m 1\u001b[0m even_results \u001b[38;5;241m=\u001b[39m [\n\u001b[1;32m 2\u001b[0m (result \u001b[38;5;241m:=\u001b[39m slow_function(i, i))\n\u001b[1;32m 3\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(\u001b[38;5;241m10\u001b[39m)\n\u001b[0;32m----> 4\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[43mresult\u001b[49m \u001b[38;5;241m%\u001b[39m \u001b[38;5;241m2\u001b[39m \u001b[38;5;241m==\u001b[39m \u001b[38;5;241m0\u001b[39m\n\u001b[1;32m 5\u001b[0m ]\n", + "\u001b[0;31mNameError\u001b[0m: name 'result' is not defined" + ] + } + ], + "source": [ + "even_results = [\n", + " (result := slow_function(i, i))\n", + " for i in range(10)\n", + " if result % 2 == 0\n", + "]" + ] + }, + { + "cell_type": "markdown", + "id": "5c0229a0", + "metadata": {}, + "source": [ + "This happens because in a list comprehension, the loop starts running, then the `if` clause (if any) is evaluated, and then the element expression is evaluated - hence why we place the assignment expression in the `if`." + ] + }, + { + "cell_type": "markdown", + "id": "cc652ad3", + "metadata": {}, + "source": [ + "**Example 2**" + ] + }, + { + "cell_type": "markdown", + "id": "093978ca", + "metadata": {}, + "source": [ + "Here's another scenario where this new operator could be quite useful." + ] + }, + { + "cell_type": "markdown", + "id": "edbdc1b7", + "metadata": {}, + "source": [ + "You want to return the result of an expression but only if it satisfies some criteria.\n", + "\n", + "For example, let's say we write a generator function to produce n even random integers between 1 and 10:" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "a75a9138", + "metadata": {}, + "outputs": [], + "source": [ + "import random\n", + "\n", + "random.seed(0)\n", + "def even_random(n):\n", + " cnt = 0\n", + " while cnt <= n:\n", + " cnt += 1\n", + " number = random.randint(0, 10)\n", + " if number % 2 == 0:\n", + " yield number" + ] + }, + { + "cell_type": "markdown", + "id": "9f19d723", + "metadata": {}, + "source": [ + "We can then call the generator function:" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "53f19b5e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[6, 6, 0, 4, 8]" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list(even_random(5))" + ] + }, + { + "cell_type": "markdown", + "id": "76ac9cf0", + "metadata": {}, + "source": [ + "We can make our code a little more concise without losing readability by using the `:=` operator:" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "b849d145", + "metadata": {}, + "outputs": [], + "source": [ + "random.seed(0)\n", + "def even_random(n):\n", + " cnt = 0\n", + " while (cnt := cnt + 1) <= n:\n", + " if (number := random.randint(0, 10)) % 2 == 0:\n", + " yield number" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "b847739e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[6, 6, 0, 4, 8]" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list(even_random(5))" + ] + }, + { + "cell_type": "markdown", + "id": "5621f621", + "metadata": {}, + "source": [ + "#### Example 3" + ] + }, + { + "cell_type": "markdown", + "id": "9e1719fd", + "metadata": {}, + "source": [ + "Here's another example where we are consuming some generator, until some condition is met." + ] + }, + { + "cell_type": "markdown", + "id": "9822e4d5", + "metadata": {}, + "source": [ + "Let's write a generator function:" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "623069a1", + "metadata": {}, + "outputs": [], + "source": [ + "def gen():\n", + " while True:\n", + " yield list(range(random.randint(0, 10)))" + ] + }, + { + "cell_type": "markdown", + "id": "da617a93", + "metadata": {}, + "source": [ + "And let's print out a frew values from this generator:" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "56b83447", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0, 1, 2]\n", + "[0, 1, 2, 3, 4]\n", + "[0, 1, 2, 3, 4, 5]\n", + "[0, 1]\n", + "[0, 1, 2]\n", + "[]\n", + "[0]\n", + "[0, 1]\n", + "[0, 1, 2]\n", + "[0, 1, 2, 3, 4, 5, 6, 7]\n" + ] + } + ], + "source": [ + "random.seed(8)\n", + "my_gen = gen()\n", + "for _ in range(10):\n", + " print(next(my_gen))" + ] + }, + { + "cell_type": "markdown", + "id": "ad1bb10e", + "metadata": {}, + "source": [ + "You'll notice that the fourth element is a list with 2 values.\n", + "\n", + "What we want to do now, is process the lists yielded by the generator, until we hit a list with two values, at which point we want to stop processing it." + ] + }, + { + "cell_type": "markdown", + "id": "25d4dab3", + "metadata": {}, + "source": [ + "We could do it this way with a `while` loop:" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "id": "f5a068ab", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0, 1, 2]\n", + "[0, 1, 2, 3, 4]\n", + "[0, 1, 2, 3, 4, 5]\n" + ] + } + ], + "source": [ + "random.seed(8)\n", + "my_gen = gen()\n", + "while True:\n", + " l = next(my_gen)\n", + " if len(l) <= 2:\n", + " break\n", + " print(l)" + ] + }, + { + "cell_type": "markdown", + "id": "fcf1f1a2", + "metadata": {}, + "source": [ + "Instead, we could re-write this way using the `:=` operator:" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "id": "7568cf9c", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0, 1, 2]\n", + "[0, 1, 2, 3, 4]\n", + "[0, 1, 2, 3, 4, 5]\n" + ] + } + ], + "source": [ + "random.seed(8)\n", + "my_gen = gen()\n", + "while len(l := next(my_gen)) > 2:\n", + " print(l)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d9f1b587", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.10.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Part 2/Section 14 - Python Updates/03 - Relevant Python 3.8_3.7 Changes.ipynb b/Part 2/Section 14 - Python Updates/04 - Relevant Python 3.8_3.7 Changes.ipynb similarity index 100% rename from Part 2/Section 14 - Python Updates/03 - Relevant Python 3.8_3.7 Changes.ipynb rename to Part 2/Section 14 - Python Updates/04 - Relevant Python 3.8_3.7 Changes.ipynb diff --git a/Part 3/Section 11 - Python Updates/03 - Python 3.8 - Assignment Expressions.ipynb b/Part 3/Section 11 - Python Updates/03 - Python 3.8 - Assignment Expressions.ipynb new file mode 100644 index 00000000..53e8b672 --- /dev/null +++ b/Part 3/Section 11 - Python Updates/03 - Python 3.8 - Assignment Expressions.ipynb @@ -0,0 +1,1082 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "17ad44da", + "metadata": {}, + "source": [ + "### Python 3.8 - Assignment Expressions" + ] + }, + { + "cell_type": "markdown", + "id": "40b0045a", + "metadata": {}, + "source": [ + "Another enhancement to the Python core language that was introduced in Python 3.8 is **assignment expressions**." + ] + }, + { + "cell_type": "markdown", + "id": "cad52d44", + "metadata": {}, + "source": [ + "You can see the pep for it here: [pep 572](https://peps.python.org/pep-0572/)" + ] + }, + { + "cell_type": "markdown", + "id": "285f5b11", + "metadata": {}, + "source": [ + "Remember that an expression is simply a snippet of code that is evaluated.\n", + "\n", + "The following are all examples of expressions:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "357659ec", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1 + 2" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "c7b05302", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[1, 2, 3, 4, 5, 6]" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "[1, 2, 3] + [4, 5, 6]" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "a68603b6", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'PYTHON'" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\" python \".lstrip().rstrip().upper()" + ] + }, + { + "cell_type": "markdown", + "id": "201b3156", + "metadata": {}, + "source": [ + "And an assignment is simply when we assign an expression result to a variable:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "b08e7b15", + "metadata": {}, + "outputs": [], + "source": [ + "a = 1 + 2\n", + "b = [1, 2, 3] + [4, 5, 6]\n", + "c = \" python \".lstrip().rstrip().upper()" + ] + }, + { + "cell_type": "markdown", + "id": "54232d7a", + "metadata": {}, + "source": [ + "As you can see, we have to different steps here.\n", + "\n", + "We assign the result of an expression (the right hand side) to a variable (the left hand side) using the equals (`=`) sign." + ] + }, + { + "cell_type": "markdown", + "id": "e0ca1920", + "metadata": {}, + "source": [ + "So we have two **distinct** (totally separate) aspects here - the expression, and the assignment." + ] + }, + { + "cell_type": "markdown", + "id": "90b2d0ab", + "metadata": {}, + "source": [ + "So what are **expression assignments**?" + ] + }, + { + "cell_type": "markdown", + "id": "d2065f05", + "metadata": {}, + "source": [ + "Expression assignments allows us to assign expressions to a variable **inside** an expression, using the `:=` operator (the so-called *walrus* operator)" + ] + }, + { + "cell_type": "markdown", + "id": "0c400c75", + "metadata": {}, + "source": [ + "Confusing? :-)" + ] + }, + { + "cell_type": "markdown", + "id": "084caec6", + "metadata": {}, + "source": [ + "Let's take a look at a very simple example first:" + ] + }, + { + "cell_type": "markdown", + "id": "985d592b", + "metadata": {}, + "source": [ + "Starting with an expression:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "c9b56fb0", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1 + 2" + ] + }, + { + "cell_type": "markdown", + "id": "b89a8762", + "metadata": {}, + "source": [ + "We could assign the result of that expression to some variable:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "225ca5f8", + "metadata": {}, + "outputs": [], + "source": [ + "a = 1 + 2" + ] + }, + { + "cell_type": "markdown", + "id": "1513437a", + "metadata": {}, + "source": [ + "But, we could also write the expression and assignment this way (not the parentheses that enclose the expression):" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "572aaf6a", + "metadata": {}, + "outputs": [], + "source": [ + "a = (1 + 2)" + ] + }, + { + "cell_type": "markdown", + "id": "8d0e2885", + "metadata": {}, + "source": [ + "With the expression assignment operator, we could actually assign the result of that expression inside the expression itself:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "0fa9c199", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(x := 1 + 2)" + ] + }, + { + "cell_type": "markdown", + "id": "1eafbee8", + "metadata": {}, + "source": [ + "As you can see, the expression returned a result (`3`), but it also assigned that result to the variable `x`:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "915d5b48", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "x" + ] + }, + { + "cell_type": "markdown", + "id": "e35a8501", + "metadata": {}, + "source": [ + "Note that the parentheses in this case are **necessary** - simply writing this would not work:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "890070de", + "metadata": {}, + "outputs": [ + { + "ename": "SyntaxError", + "evalue": "invalid syntax (746607632.py, line 1)", + "output_type": "error", + "traceback": [ + "\u001b[0;36m Input \u001b[0;32mIn [10]\u001b[0;36m\u001b[0m\n\u001b[0;31m x := 1 + 2\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n" + ] + } + ], + "source": [ + "x := 1 + 2" + ] + }, + { + "cell_type": "markdown", + "id": "d0b5f281", + "metadata": {}, + "source": [ + "This is because the `:=` operator must be used inside an **expression**, so we can force it by using the parentheses." + ] + }, + { + "cell_type": "markdown", + "id": "3195a0a8", + "metadata": {}, + "source": [ + "We could even do this:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "6d1e232c", + "metadata": {}, + "outputs": [], + "source": [ + "a = (x := 10 + 20)" + ] + }, + { + "cell_type": "markdown", + "id": "35a7ed36", + "metadata": {}, + "source": [ + "Then, `a`, and `x` are:" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "96bf1532", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(30, 30)" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a, x" + ] + }, + { + "cell_type": "markdown", + "id": "638c195a", + "metadata": {}, + "source": [ + "Yeah, even more confusing! But in a minute I'll show you why this can be very useful." + ] + }, + { + "cell_type": "markdown", + "id": "4a625536", + "metadata": {}, + "source": [ + "Before we move on to that, let's see how this assignment expression works when we deal with mutable objects such as lists:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "a2ee53ae", + "metadata": {}, + "outputs": [], + "source": [ + "l1 = (l2 := [1, 2] + [3, 4])" + ] + }, + { + "cell_type": "markdown", + "id": "6e58d69f", + "metadata": {}, + "source": [ + "Here, `l1` was the result of the concatenation of the two lists:" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "2d3ae1a4", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "([1, 2, 3, 4], 4393044992)" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "l1, id(l1)" + ] + }, + { + "cell_type": "markdown", + "id": "88aee44a", + "metadata": {}, + "source": [ + "But what about `l2`? It should be a list with the same values, but is it the same object reference as `l1`?" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "fc8fe138", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "([1, 2, 3, 4], 4393044992)" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "l2, id(l2)" + ] + }, + { + "cell_type": "markdown", + "id": "243f0d28", + "metadata": {}, + "source": [ + "And indeed, they are not only the same values, but the same object." + ] + }, + { + "cell_type": "markdown", + "id": "e7268ec5", + "metadata": {}, + "source": [ + "Usually this is not an issue, but keep it in mind because you end up with shared references that you may not realize exist." + ] + }, + { + "cell_type": "markdown", + "id": "e27c849e", + "metadata": {}, + "source": [ + "So now, why is this useful?" + ] + }, + { + "cell_type": "markdown", + "id": "a57d9679", + "metadata": {}, + "source": [ + "Often, we end up writing expressions in terms of other sub expressions, not just for clarity, but sometimes to **avoid repeating** function calls or expression evaluations." + ] + }, + { + "cell_type": "markdown", + "id": "9a686538", + "metadata": {}, + "source": [ + "#### Example 1" + ] + }, + { + "cell_type": "markdown", + "id": "3131d1f1", + "metadata": {}, + "source": [ + "Suppose we have some long running function:" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "f66dd3b5", + "metadata": {}, + "outputs": [], + "source": [ + "import time\n", + "import math\n", + "\n", + "def slow_function(x, y):\n", + " time.sleep(0.5)\n", + " return round(math.sqrt(x**2 + y**2))" + ] + }, + { + "cell_type": "markdown", + "id": "9ddce261", + "metadata": {}, + "source": [ + "Now executing this function will take about 2 seconds to run every time it is called, even when calling it with the same values (we could of course use some LRU caching, but only if the function is **deterministic** - if the function is reading data from a web site, or a database, the result for the same arguments may not be the same and so LRU caching is not even a viable option in this case).\n", + "\n", + "> A **deterministic** function is a function that for the **same** inputs, always returns the **same** result:\n", + ">\n", + "> Obviously the function we have above is deterministic, but this one would not be:\n", + ">```\n", + ">def get_price(symbol):\n", + "> # query an API for latest price for symbol\n", + "> price = ...\n", + "> return price\n", + ">``` \n", + "> A function that returns the current date or time, or a random number, etc are all non-deterministic functions." + ] + }, + { + "cell_type": "markdown", + "id": "6e503d9b", + "metadata": {}, + "source": [ + "So, LRU caching is not always an option." + ] + }, + { + "cell_type": "markdown", + "id": "05027bcc", + "metadata": {}, + "source": [ + "Let's see an example of why we would want to assign the result of our long running function to a variable, instead of just using it directly." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "a9babf8f", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0, 4, 6, 8, 10]\n", + "Elapsed: 7.6 seconds\n" + ] + } + ], + "source": [ + "from time import perf_counter\n", + "\n", + "start = perf_counter()\n", + "even_results = []\n", + "for i in range(10):\n", + " if slow_function(i, i) % 2 == 0:\n", + " even_results.append(slow_function(i, i))\n", + "end = perf_counter()\n", + "print(even_results)\n", + "print(f'Elapsed: {end - start:.1f} seconds')" + ] + }, + { + "cell_type": "markdown", + "id": "3bdd4e15", + "metadata": {}, + "source": [ + "Well that was painfully slow!\n", + "\n", + "But notice that we are calling the same function, with the same arguments twice - we can eliminate that!" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "19453896", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0, 4, 6, 8, 10]\n", + "Elapsed: 5.0 seconds\n" + ] + } + ], + "source": [ + "start = perf_counter()\n", + "even_results = []\n", + "for i in range(10):\n", + " result = slow_function(i, i)\n", + " if result % 2 == 0:\n", + " even_results.append(result)\n", + "end = perf_counter()\n", + "print(even_results)\n", + "print(f'Elapsed: {end - start:.1f} seconds')" + ] + }, + { + "cell_type": "markdown", + "id": "3b6091cb", + "metadata": {}, + "source": [ + "So we are able to speed this code up, by using that interim `result` variable - also note how `result` is basically a throw away variable (we typically would not use such a variable outside the loop itself - exceptions happen of course)." + ] + }, + { + "cell_type": "markdown", + "id": "8acf631f", + "metadata": {}, + "source": [ + "But notice something about that code? It's ugly looking - we are building up a list by running through a loop and adding to an initially empty list, one element at a time." + ] + }, + { + "cell_type": "markdown", + "id": "6293f28c", + "metadata": {}, + "source": [ + "We can do better! List comprehensions of course." + ] + }, + { + "cell_type": "markdown", + "id": "9cdff5a7", + "metadata": {}, + "source": [ + "But... we can't write that `result = slow_function(i, i)` in our list comprehension - so we would be back to the original (and slower) may of doing it:" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "0d89b92d", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0, 4, 6, 8, 10]\n", + "Elapsed: 7.6 seconds\n" + ] + } + ], + "source": [ + "start = perf_counter()\n", + "even_results = [\n", + " slow_function(i, i)\n", + " for i in range(10)\n", + " if slow_function(i, i) % 2 == 0\n", + "]\n", + "end = perf_counter()\n", + "print(even_results)\n", + "print(f'Elapsed: {end - start:.1f} seconds')" + ] + }, + { + "cell_type": "markdown", + "id": "8db81b84", + "metadata": {}, + "source": [ + ":-(" + ] + }, + { + "cell_type": "markdown", + "id": "6c42d90a", + "metadata": {}, + "source": [ + "And this is where the assignment expression operator comes in very handy:" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "eefb7ce7", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0, 4, 6, 8, 10]\n", + "Elapsed: 5.0 seconds\n" + ] + } + ], + "source": [ + "start = perf_counter()\n", + "even_results = [\n", + " result\n", + " for i in range(10)\n", + " if (result := slow_function(i, i)) % 2 == 0\n", + "]\n", + "end = perf_counter()\n", + "print(even_results)\n", + "print(f'Elapsed: {end - start:.1f} seconds')" + ] + }, + { + "cell_type": "markdown", + "id": "c1c77b0c", + "metadata": {}, + "source": [ + "Notice how using the `:=` operator, we assign the result of `slow_function(i, i)` to `result` as part of the expression itself, and then re-use that computed value for the elements of the list." + ] + }, + { + "cell_type": "markdown", + "id": "a9e3744d", + "metadata": {}, + "source": [ + "You may be asking yourself, why not write it this way:" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "db2bc22b", + "metadata": {}, + "outputs": [], + "source": [ + "del result" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "cd112bb8", + "metadata": {}, + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'result' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", + "Input \u001b[0;32mIn [22]\u001b[0m, in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0m even_results \u001b[38;5;241m=\u001b[39m [\n\u001b[1;32m 2\u001b[0m (result \u001b[38;5;241m:=\u001b[39m slow_function(i, i))\n\u001b[1;32m 3\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(\u001b[38;5;241m10\u001b[39m)\n\u001b[1;32m 4\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m result \u001b[38;5;241m%\u001b[39m \u001b[38;5;241m2\u001b[39m \u001b[38;5;241m==\u001b[39m \u001b[38;5;241m0\u001b[39m\n\u001b[1;32m 5\u001b[0m ]\n", + "Input \u001b[0;32mIn [22]\u001b[0m, in \u001b[0;36m\u001b[0;34m(.0)\u001b[0m\n\u001b[1;32m 1\u001b[0m even_results \u001b[38;5;241m=\u001b[39m [\n\u001b[1;32m 2\u001b[0m (result \u001b[38;5;241m:=\u001b[39m slow_function(i, i))\n\u001b[1;32m 3\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(\u001b[38;5;241m10\u001b[39m)\n\u001b[0;32m----> 4\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[43mresult\u001b[49m \u001b[38;5;241m%\u001b[39m \u001b[38;5;241m2\u001b[39m \u001b[38;5;241m==\u001b[39m \u001b[38;5;241m0\u001b[39m\n\u001b[1;32m 5\u001b[0m ]\n", + "\u001b[0;31mNameError\u001b[0m: name 'result' is not defined" + ] + } + ], + "source": [ + "even_results = [\n", + " (result := slow_function(i, i))\n", + " for i in range(10)\n", + " if result % 2 == 0\n", + "]" + ] + }, + { + "cell_type": "markdown", + "id": "5c0229a0", + "metadata": {}, + "source": [ + "This happens because in a list comprehension, the loop starts running, then the `if` clause (if any) is evaluated, and then the element expression is evaluated - hence why we place the assignment expression in the `if`." + ] + }, + { + "cell_type": "markdown", + "id": "cc652ad3", + "metadata": {}, + "source": [ + "**Example 2**" + ] + }, + { + "cell_type": "markdown", + "id": "093978ca", + "metadata": {}, + "source": [ + "Here's another scenario where this new operator could be quite useful." + ] + }, + { + "cell_type": "markdown", + "id": "edbdc1b7", + "metadata": {}, + "source": [ + "You want to return the result of an expression but only if it satisfies some criteria.\n", + "\n", + "For example, let's say we write a generator function to produce n even random integers between 1 and 10:" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "a75a9138", + "metadata": {}, + "outputs": [], + "source": [ + "import random\n", + "\n", + "random.seed(0)\n", + "def even_random(n):\n", + " cnt = 0\n", + " while cnt <= n:\n", + " cnt += 1\n", + " number = random.randint(0, 10)\n", + " if number % 2 == 0:\n", + " yield number" + ] + }, + { + "cell_type": "markdown", + "id": "9f19d723", + "metadata": {}, + "source": [ + "We can then call the generator function:" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "53f19b5e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[6, 6, 0, 4, 8]" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list(even_random(5))" + ] + }, + { + "cell_type": "markdown", + "id": "76ac9cf0", + "metadata": {}, + "source": [ + "We can make our code a little more concise without losing readability by using the `:=` operator:" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "b849d145", + "metadata": {}, + "outputs": [], + "source": [ + "random.seed(0)\n", + "def even_random(n):\n", + " cnt = 0\n", + " while (cnt := cnt + 1) <= n:\n", + " if (number := random.randint(0, 10)) % 2 == 0:\n", + " yield number" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "b847739e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[6, 6, 0, 4, 8]" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list(even_random(5))" + ] + }, + { + "cell_type": "markdown", + "id": "5621f621", + "metadata": {}, + "source": [ + "#### Example 3" + ] + }, + { + "cell_type": "markdown", + "id": "9e1719fd", + "metadata": {}, + "source": [ + "Here's another example where we are consuming some generator, until some condition is met." + ] + }, + { + "cell_type": "markdown", + "id": "9822e4d5", + "metadata": {}, + "source": [ + "Let's write a generator function:" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "623069a1", + "metadata": {}, + "outputs": [], + "source": [ + "def gen():\n", + " while True:\n", + " yield list(range(random.randint(0, 10)))" + ] + }, + { + "cell_type": "markdown", + "id": "da617a93", + "metadata": {}, + "source": [ + "And let's print out a frew values from this generator:" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "56b83447", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0, 1, 2]\n", + "[0, 1, 2, 3, 4]\n", + "[0, 1, 2, 3, 4, 5]\n", + "[0, 1]\n", + "[0, 1, 2]\n", + "[]\n", + "[0]\n", + "[0, 1]\n", + "[0, 1, 2]\n", + "[0, 1, 2, 3, 4, 5, 6, 7]\n" + ] + } + ], + "source": [ + "random.seed(8)\n", + "my_gen = gen()\n", + "for _ in range(10):\n", + " print(next(my_gen))" + ] + }, + { + "cell_type": "markdown", + "id": "ad1bb10e", + "metadata": {}, + "source": [ + "You'll notice that the fourth element is a list with 2 values.\n", + "\n", + "What we want to do now, is process the lists yielded by the generator, until we hit a list with two values, at which point we want to stop processing it." + ] + }, + { + "cell_type": "markdown", + "id": "25d4dab3", + "metadata": {}, + "source": [ + "We could do it this way with a `while` loop:" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "id": "f5a068ab", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0, 1, 2]\n", + "[0, 1, 2, 3, 4]\n", + "[0, 1, 2, 3, 4, 5]\n" + ] + } + ], + "source": [ + "random.seed(8)\n", + "my_gen = gen()\n", + "while True:\n", + " l = next(my_gen)\n", + " if len(l) <= 2:\n", + " break\n", + " print(l)" + ] + }, + { + "cell_type": "markdown", + "id": "fcf1f1a2", + "metadata": {}, + "source": [ + "Instead, we could re-write this way using the `:=` operator:" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "id": "7568cf9c", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0, 1, 2]\n", + "[0, 1, 2, 3, 4]\n", + "[0, 1, 2, 3, 4, 5]\n" + ] + } + ], + "source": [ + "random.seed(8)\n", + "my_gen = gen()\n", + "while len(l := next(my_gen)) > 2:\n", + " print(l)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d9f1b587", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.10.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Part 3/Section 11 - Python Updates/03 - Relevant Python 3.8_3.7 Changes.ipynb b/Part 3/Section 11 - Python Updates/04 - Relevant Python 3.8_3.7 Changes.ipynb similarity index 100% rename from Part 3/Section 11 - Python Updates/03 - Relevant Python 3.8_3.7 Changes.ipynb rename to Part 3/Section 11 - Python Updates/04 - Relevant Python 3.8_3.7 Changes.ipynb diff --git a/Part 4/Section 15 - Python Updates/03 - Python 3.8 - Assignment Expressions.ipynb b/Part 4/Section 15 - Python Updates/03 - Python 3.8 - Assignment Expressions.ipynb new file mode 100644 index 00000000..53e8b672 --- /dev/null +++ b/Part 4/Section 15 - Python Updates/03 - Python 3.8 - Assignment Expressions.ipynb @@ -0,0 +1,1082 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "17ad44da", + "metadata": {}, + "source": [ + "### Python 3.8 - Assignment Expressions" + ] + }, + { + "cell_type": "markdown", + "id": "40b0045a", + "metadata": {}, + "source": [ + "Another enhancement to the Python core language that was introduced in Python 3.8 is **assignment expressions**." + ] + }, + { + "cell_type": "markdown", + "id": "cad52d44", + "metadata": {}, + "source": [ + "You can see the pep for it here: [pep 572](https://peps.python.org/pep-0572/)" + ] + }, + { + "cell_type": "markdown", + "id": "285f5b11", + "metadata": {}, + "source": [ + "Remember that an expression is simply a snippet of code that is evaluated.\n", + "\n", + "The following are all examples of expressions:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "357659ec", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1 + 2" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "c7b05302", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[1, 2, 3, 4, 5, 6]" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "[1, 2, 3] + [4, 5, 6]" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "a68603b6", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'PYTHON'" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\" python \".lstrip().rstrip().upper()" + ] + }, + { + "cell_type": "markdown", + "id": "201b3156", + "metadata": {}, + "source": [ + "And an assignment is simply when we assign an expression result to a variable:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "b08e7b15", + "metadata": {}, + "outputs": [], + "source": [ + "a = 1 + 2\n", + "b = [1, 2, 3] + [4, 5, 6]\n", + "c = \" python \".lstrip().rstrip().upper()" + ] + }, + { + "cell_type": "markdown", + "id": "54232d7a", + "metadata": {}, + "source": [ + "As you can see, we have to different steps here.\n", + "\n", + "We assign the result of an expression (the right hand side) to a variable (the left hand side) using the equals (`=`) sign." + ] + }, + { + "cell_type": "markdown", + "id": "e0ca1920", + "metadata": {}, + "source": [ + "So we have two **distinct** (totally separate) aspects here - the expression, and the assignment." + ] + }, + { + "cell_type": "markdown", + "id": "90b2d0ab", + "metadata": {}, + "source": [ + "So what are **expression assignments**?" + ] + }, + { + "cell_type": "markdown", + "id": "d2065f05", + "metadata": {}, + "source": [ + "Expression assignments allows us to assign expressions to a variable **inside** an expression, using the `:=` operator (the so-called *walrus* operator)" + ] + }, + { + "cell_type": "markdown", + "id": "0c400c75", + "metadata": {}, + "source": [ + "Confusing? :-)" + ] + }, + { + "cell_type": "markdown", + "id": "084caec6", + "metadata": {}, + "source": [ + "Let's take a look at a very simple example first:" + ] + }, + { + "cell_type": "markdown", + "id": "985d592b", + "metadata": {}, + "source": [ + "Starting with an expression:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "c9b56fb0", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1 + 2" + ] + }, + { + "cell_type": "markdown", + "id": "b89a8762", + "metadata": {}, + "source": [ + "We could assign the result of that expression to some variable:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "225ca5f8", + "metadata": {}, + "outputs": [], + "source": [ + "a = 1 + 2" + ] + }, + { + "cell_type": "markdown", + "id": "1513437a", + "metadata": {}, + "source": [ + "But, we could also write the expression and assignment this way (not the parentheses that enclose the expression):" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "572aaf6a", + "metadata": {}, + "outputs": [], + "source": [ + "a = (1 + 2)" + ] + }, + { + "cell_type": "markdown", + "id": "8d0e2885", + "metadata": {}, + "source": [ + "With the expression assignment operator, we could actually assign the result of that expression inside the expression itself:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "0fa9c199", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(x := 1 + 2)" + ] + }, + { + "cell_type": "markdown", + "id": "1eafbee8", + "metadata": {}, + "source": [ + "As you can see, the expression returned a result (`3`), but it also assigned that result to the variable `x`:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "915d5b48", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "x" + ] + }, + { + "cell_type": "markdown", + "id": "e35a8501", + "metadata": {}, + "source": [ + "Note that the parentheses in this case are **necessary** - simply writing this would not work:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "890070de", + "metadata": {}, + "outputs": [ + { + "ename": "SyntaxError", + "evalue": "invalid syntax (746607632.py, line 1)", + "output_type": "error", + "traceback": [ + "\u001b[0;36m Input \u001b[0;32mIn [10]\u001b[0;36m\u001b[0m\n\u001b[0;31m x := 1 + 2\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n" + ] + } + ], + "source": [ + "x := 1 + 2" + ] + }, + { + "cell_type": "markdown", + "id": "d0b5f281", + "metadata": {}, + "source": [ + "This is because the `:=` operator must be used inside an **expression**, so we can force it by using the parentheses." + ] + }, + { + "cell_type": "markdown", + "id": "3195a0a8", + "metadata": {}, + "source": [ + "We could even do this:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "6d1e232c", + "metadata": {}, + "outputs": [], + "source": [ + "a = (x := 10 + 20)" + ] + }, + { + "cell_type": "markdown", + "id": "35a7ed36", + "metadata": {}, + "source": [ + "Then, `a`, and `x` are:" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "96bf1532", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(30, 30)" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a, x" + ] + }, + { + "cell_type": "markdown", + "id": "638c195a", + "metadata": {}, + "source": [ + "Yeah, even more confusing! But in a minute I'll show you why this can be very useful." + ] + }, + { + "cell_type": "markdown", + "id": "4a625536", + "metadata": {}, + "source": [ + "Before we move on to that, let's see how this assignment expression works when we deal with mutable objects such as lists:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "a2ee53ae", + "metadata": {}, + "outputs": [], + "source": [ + "l1 = (l2 := [1, 2] + [3, 4])" + ] + }, + { + "cell_type": "markdown", + "id": "6e58d69f", + "metadata": {}, + "source": [ + "Here, `l1` was the result of the concatenation of the two lists:" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "2d3ae1a4", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "([1, 2, 3, 4], 4393044992)" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "l1, id(l1)" + ] + }, + { + "cell_type": "markdown", + "id": "88aee44a", + "metadata": {}, + "source": [ + "But what about `l2`? It should be a list with the same values, but is it the same object reference as `l1`?" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "fc8fe138", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "([1, 2, 3, 4], 4393044992)" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "l2, id(l2)" + ] + }, + { + "cell_type": "markdown", + "id": "243f0d28", + "metadata": {}, + "source": [ + "And indeed, they are not only the same values, but the same object." + ] + }, + { + "cell_type": "markdown", + "id": "e7268ec5", + "metadata": {}, + "source": [ + "Usually this is not an issue, but keep it in mind because you end up with shared references that you may not realize exist." + ] + }, + { + "cell_type": "markdown", + "id": "e27c849e", + "metadata": {}, + "source": [ + "So now, why is this useful?" + ] + }, + { + "cell_type": "markdown", + "id": "a57d9679", + "metadata": {}, + "source": [ + "Often, we end up writing expressions in terms of other sub expressions, not just for clarity, but sometimes to **avoid repeating** function calls or expression evaluations." + ] + }, + { + "cell_type": "markdown", + "id": "9a686538", + "metadata": {}, + "source": [ + "#### Example 1" + ] + }, + { + "cell_type": "markdown", + "id": "3131d1f1", + "metadata": {}, + "source": [ + "Suppose we have some long running function:" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "f66dd3b5", + "metadata": {}, + "outputs": [], + "source": [ + "import time\n", + "import math\n", + "\n", + "def slow_function(x, y):\n", + " time.sleep(0.5)\n", + " return round(math.sqrt(x**2 + y**2))" + ] + }, + { + "cell_type": "markdown", + "id": "9ddce261", + "metadata": {}, + "source": [ + "Now executing this function will take about 2 seconds to run every time it is called, even when calling it with the same values (we could of course use some LRU caching, but only if the function is **deterministic** - if the function is reading data from a web site, or a database, the result for the same arguments may not be the same and so LRU caching is not even a viable option in this case).\n", + "\n", + "> A **deterministic** function is a function that for the **same** inputs, always returns the **same** result:\n", + ">\n", + "> Obviously the function we have above is deterministic, but this one would not be:\n", + ">```\n", + ">def get_price(symbol):\n", + "> # query an API for latest price for symbol\n", + "> price = ...\n", + "> return price\n", + ">``` \n", + "> A function that returns the current date or time, or a random number, etc are all non-deterministic functions." + ] + }, + { + "cell_type": "markdown", + "id": "6e503d9b", + "metadata": {}, + "source": [ + "So, LRU caching is not always an option." + ] + }, + { + "cell_type": "markdown", + "id": "05027bcc", + "metadata": {}, + "source": [ + "Let's see an example of why we would want to assign the result of our long running function to a variable, instead of just using it directly." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "a9babf8f", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0, 4, 6, 8, 10]\n", + "Elapsed: 7.6 seconds\n" + ] + } + ], + "source": [ + "from time import perf_counter\n", + "\n", + "start = perf_counter()\n", + "even_results = []\n", + "for i in range(10):\n", + " if slow_function(i, i) % 2 == 0:\n", + " even_results.append(slow_function(i, i))\n", + "end = perf_counter()\n", + "print(even_results)\n", + "print(f'Elapsed: {end - start:.1f} seconds')" + ] + }, + { + "cell_type": "markdown", + "id": "3bdd4e15", + "metadata": {}, + "source": [ + "Well that was painfully slow!\n", + "\n", + "But notice that we are calling the same function, with the same arguments twice - we can eliminate that!" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "19453896", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0, 4, 6, 8, 10]\n", + "Elapsed: 5.0 seconds\n" + ] + } + ], + "source": [ + "start = perf_counter()\n", + "even_results = []\n", + "for i in range(10):\n", + " result = slow_function(i, i)\n", + " if result % 2 == 0:\n", + " even_results.append(result)\n", + "end = perf_counter()\n", + "print(even_results)\n", + "print(f'Elapsed: {end - start:.1f} seconds')" + ] + }, + { + "cell_type": "markdown", + "id": "3b6091cb", + "metadata": {}, + "source": [ + "So we are able to speed this code up, by using that interim `result` variable - also note how `result` is basically a throw away variable (we typically would not use such a variable outside the loop itself - exceptions happen of course)." + ] + }, + { + "cell_type": "markdown", + "id": "8acf631f", + "metadata": {}, + "source": [ + "But notice something about that code? It's ugly looking - we are building up a list by running through a loop and adding to an initially empty list, one element at a time." + ] + }, + { + "cell_type": "markdown", + "id": "6293f28c", + "metadata": {}, + "source": [ + "We can do better! List comprehensions of course." + ] + }, + { + "cell_type": "markdown", + "id": "9cdff5a7", + "metadata": {}, + "source": [ + "But... we can't write that `result = slow_function(i, i)` in our list comprehension - so we would be back to the original (and slower) may of doing it:" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "0d89b92d", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0, 4, 6, 8, 10]\n", + "Elapsed: 7.6 seconds\n" + ] + } + ], + "source": [ + "start = perf_counter()\n", + "even_results = [\n", + " slow_function(i, i)\n", + " for i in range(10)\n", + " if slow_function(i, i) % 2 == 0\n", + "]\n", + "end = perf_counter()\n", + "print(even_results)\n", + "print(f'Elapsed: {end - start:.1f} seconds')" + ] + }, + { + "cell_type": "markdown", + "id": "8db81b84", + "metadata": {}, + "source": [ + ":-(" + ] + }, + { + "cell_type": "markdown", + "id": "6c42d90a", + "metadata": {}, + "source": [ + "And this is where the assignment expression operator comes in very handy:" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "eefb7ce7", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0, 4, 6, 8, 10]\n", + "Elapsed: 5.0 seconds\n" + ] + } + ], + "source": [ + "start = perf_counter()\n", + "even_results = [\n", + " result\n", + " for i in range(10)\n", + " if (result := slow_function(i, i)) % 2 == 0\n", + "]\n", + "end = perf_counter()\n", + "print(even_results)\n", + "print(f'Elapsed: {end - start:.1f} seconds')" + ] + }, + { + "cell_type": "markdown", + "id": "c1c77b0c", + "metadata": {}, + "source": [ + "Notice how using the `:=` operator, we assign the result of `slow_function(i, i)` to `result` as part of the expression itself, and then re-use that computed value for the elements of the list." + ] + }, + { + "cell_type": "markdown", + "id": "a9e3744d", + "metadata": {}, + "source": [ + "You may be asking yourself, why not write it this way:" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "db2bc22b", + "metadata": {}, + "outputs": [], + "source": [ + "del result" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "cd112bb8", + "metadata": {}, + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'result' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", + "Input \u001b[0;32mIn [22]\u001b[0m, in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0m even_results \u001b[38;5;241m=\u001b[39m [\n\u001b[1;32m 2\u001b[0m (result \u001b[38;5;241m:=\u001b[39m slow_function(i, i))\n\u001b[1;32m 3\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(\u001b[38;5;241m10\u001b[39m)\n\u001b[1;32m 4\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m result \u001b[38;5;241m%\u001b[39m \u001b[38;5;241m2\u001b[39m \u001b[38;5;241m==\u001b[39m \u001b[38;5;241m0\u001b[39m\n\u001b[1;32m 5\u001b[0m ]\n", + "Input \u001b[0;32mIn [22]\u001b[0m, in \u001b[0;36m\u001b[0;34m(.0)\u001b[0m\n\u001b[1;32m 1\u001b[0m even_results \u001b[38;5;241m=\u001b[39m [\n\u001b[1;32m 2\u001b[0m (result \u001b[38;5;241m:=\u001b[39m slow_function(i, i))\n\u001b[1;32m 3\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(\u001b[38;5;241m10\u001b[39m)\n\u001b[0;32m----> 4\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[43mresult\u001b[49m \u001b[38;5;241m%\u001b[39m \u001b[38;5;241m2\u001b[39m \u001b[38;5;241m==\u001b[39m \u001b[38;5;241m0\u001b[39m\n\u001b[1;32m 5\u001b[0m ]\n", + "\u001b[0;31mNameError\u001b[0m: name 'result' is not defined" + ] + } + ], + "source": [ + "even_results = [\n", + " (result := slow_function(i, i))\n", + " for i in range(10)\n", + " if result % 2 == 0\n", + "]" + ] + }, + { + "cell_type": "markdown", + "id": "5c0229a0", + "metadata": {}, + "source": [ + "This happens because in a list comprehension, the loop starts running, then the `if` clause (if any) is evaluated, and then the element expression is evaluated - hence why we place the assignment expression in the `if`." + ] + }, + { + "cell_type": "markdown", + "id": "cc652ad3", + "metadata": {}, + "source": [ + "**Example 2**" + ] + }, + { + "cell_type": "markdown", + "id": "093978ca", + "metadata": {}, + "source": [ + "Here's another scenario where this new operator could be quite useful." + ] + }, + { + "cell_type": "markdown", + "id": "edbdc1b7", + "metadata": {}, + "source": [ + "You want to return the result of an expression but only if it satisfies some criteria.\n", + "\n", + "For example, let's say we write a generator function to produce n even random integers between 1 and 10:" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "a75a9138", + "metadata": {}, + "outputs": [], + "source": [ + "import random\n", + "\n", + "random.seed(0)\n", + "def even_random(n):\n", + " cnt = 0\n", + " while cnt <= n:\n", + " cnt += 1\n", + " number = random.randint(0, 10)\n", + " if number % 2 == 0:\n", + " yield number" + ] + }, + { + "cell_type": "markdown", + "id": "9f19d723", + "metadata": {}, + "source": [ + "We can then call the generator function:" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "53f19b5e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[6, 6, 0, 4, 8]" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list(even_random(5))" + ] + }, + { + "cell_type": "markdown", + "id": "76ac9cf0", + "metadata": {}, + "source": [ + "We can make our code a little more concise without losing readability by using the `:=` operator:" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "b849d145", + "metadata": {}, + "outputs": [], + "source": [ + "random.seed(0)\n", + "def even_random(n):\n", + " cnt = 0\n", + " while (cnt := cnt + 1) <= n:\n", + " if (number := random.randint(0, 10)) % 2 == 0:\n", + " yield number" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "b847739e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[6, 6, 0, 4, 8]" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list(even_random(5))" + ] + }, + { + "cell_type": "markdown", + "id": "5621f621", + "metadata": {}, + "source": [ + "#### Example 3" + ] + }, + { + "cell_type": "markdown", + "id": "9e1719fd", + "metadata": {}, + "source": [ + "Here's another example where we are consuming some generator, until some condition is met." + ] + }, + { + "cell_type": "markdown", + "id": "9822e4d5", + "metadata": {}, + "source": [ + "Let's write a generator function:" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "623069a1", + "metadata": {}, + "outputs": [], + "source": [ + "def gen():\n", + " while True:\n", + " yield list(range(random.randint(0, 10)))" + ] + }, + { + "cell_type": "markdown", + "id": "da617a93", + "metadata": {}, + "source": [ + "And let's print out a frew values from this generator:" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "56b83447", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0, 1, 2]\n", + "[0, 1, 2, 3, 4]\n", + "[0, 1, 2, 3, 4, 5]\n", + "[0, 1]\n", + "[0, 1, 2]\n", + "[]\n", + "[0]\n", + "[0, 1]\n", + "[0, 1, 2]\n", + "[0, 1, 2, 3, 4, 5, 6, 7]\n" + ] + } + ], + "source": [ + "random.seed(8)\n", + "my_gen = gen()\n", + "for _ in range(10):\n", + " print(next(my_gen))" + ] + }, + { + "cell_type": "markdown", + "id": "ad1bb10e", + "metadata": {}, + "source": [ + "You'll notice that the fourth element is a list with 2 values.\n", + "\n", + "What we want to do now, is process the lists yielded by the generator, until we hit a list with two values, at which point we want to stop processing it." + ] + }, + { + "cell_type": "markdown", + "id": "25d4dab3", + "metadata": {}, + "source": [ + "We could do it this way with a `while` loop:" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "id": "f5a068ab", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0, 1, 2]\n", + "[0, 1, 2, 3, 4]\n", + "[0, 1, 2, 3, 4, 5]\n" + ] + } + ], + "source": [ + "random.seed(8)\n", + "my_gen = gen()\n", + "while True:\n", + " l = next(my_gen)\n", + " if len(l) <= 2:\n", + " break\n", + " print(l)" + ] + }, + { + "cell_type": "markdown", + "id": "fcf1f1a2", + "metadata": {}, + "source": [ + "Instead, we could re-write this way using the `:=` operator:" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "id": "7568cf9c", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0, 1, 2]\n", + "[0, 1, 2, 3, 4]\n", + "[0, 1, 2, 3, 4, 5]\n" + ] + } + ], + "source": [ + "random.seed(8)\n", + "my_gen = gen()\n", + "while len(l := next(my_gen)) > 2:\n", + " print(l)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d9f1b587", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.10.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Part 4/Section 15 - Python Updates/03 - Relevant Python 3.8_3.7 Changes.ipynb b/Part 4/Section 15 - Python Updates/04 - Relevant Python 3.8_3.7 Changes.ipynb similarity index 100% rename from Part 4/Section 15 - Python Updates/03 - Relevant Python 3.8_3.7 Changes.ipynb rename to Part 4/Section 15 - Python Updates/04 - Relevant Python 3.8_3.7 Changes.ipynb From 200a24ef25dca24117397671c2a21e7a73b414e3 Mon Sep 17 00:00:00 2001 From: fbaptiste <6551123+fbaptiste@users.noreply.github.com> Date: Sun, 13 Nov 2022 07:30:26 -0700 Subject: [PATCH 15/27] fixed notebook --- ...sing Iterators as Function Arguments.ipynb | 2623 +++++++++++++++++ ...g Iterators for Function Arguments.tscproj | 206 -- 2 files changed, 2623 insertions(+), 206 deletions(-) create mode 100644 Part 2/Section 04 - Iterables and Iterators/13 - Caveat Using Iterators as Function Arguments.ipynb delete mode 100644 Part 2/Section 04 - Iterables and Iterators/13 - Caveat Using Iterators for Function Arguments.tscproj diff --git a/Part 2/Section 04 - Iterables and Iterators/13 - Caveat Using Iterators as Function Arguments.ipynb b/Part 2/Section 04 - Iterables and Iterators/13 - Caveat Using Iterators as Function Arguments.ipynb new file mode 100644 index 00000000..7035375c --- /dev/null +++ b/Part 2/Section 04 - Iterables and Iterators/13 - Caveat Using Iterators as Function Arguments.ipynb @@ -0,0 +1,2623 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Caveat of Using Iterators as Function Arguments" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "When a function requires an iterable for one of its arguments, it will also work with any iterator (since iterators are themselves iterables).\n", + "\n", + "But things can go wrong if you do that!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's say we have an iterator that returns a collection of random numbers, and we want, for each such collection, find the minimum amd maximum value:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "import random" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "class Randoms:\n", + " def __init__(self, n):\n", + " self.n = n\n", + " self.i = 0\n", + " \n", + " def __iter__(self):\n", + " return self\n", + " \n", + " def __next__(self):\n", + " if self.i >= self.n:\n", + " raise StopIteration\n", + " else:\n", + " self.i += 1\n", + " return random.randint(0, 100)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[49, 97, 53, 5, 33, 65, 62, 51, 100, 38]\n" + ] + } + ], + "source": [ + "random.seed(0)\n", + "l = list(Randoms(10))\n", + "print(l)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we can easily find the min and max values:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(5, 100)" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "min(l), max(l)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "But watch what happens if we do this:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "random.seed(0)\n", + "l = Randoms(10)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "5" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "min(l)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "ename": "ValueError", + "evalue": "max() arg is an empty sequence", + "output_type": "error", + "traceback": [ + "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[1;31mValueError\u001b[0m Traceback (most recent call last)", + "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m()\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mmax\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0ml\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[1;31mValueError\u001b[0m: max() arg is an empty sequence" + ] + } + ], + "source": [ + "max(l)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "That's because when `min` ran, it iterated over the **iterator** `Randoms(10)`. When we called `max` on the same iterator, it had already been exhausted - i.e. the argument to max was now empty!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "So, be really careful when using iterators!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here's another more practical example.\n", + "\n", + "Let's go back to our `cars.csv` data file and write some code that will return the car names and MPG - except we also want to return a value indicating the percentage of the car's MPG to the least fuel efficient car in the list.\n", + "\n", + "To do so we will need to iterate over the file twice - once to figure out the largest MPG value, and another time to make the calculation MPG/min_mpg * 100." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's just quickly see what our file looks like:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Car;MPG;Cylinders;Displacement;Horsepower;Weight;Acceleration;Model;Origin\n", + "STRING;DOUBLE;INT;DOUBLE;DOUBLE;DOUBLE;DOUBLE;INT;CAT\n", + "Chevrolet Chevelle Malibu;18.0;8;307.0;130.0;3504.;12.0;70;US\n", + "Buick Skylark 320;15.0;8;350.0;165.0;3693.;11.5;70;US\n", + "Plymouth Satellite;18.0;8;318.0;150.0;3436.;11.0;70;US\n", + "AMC Rebel SST;16.0;8;304.0;150.0;3433.;12.0;70;US\n", + "Ford Torino;17.0;8;302.0;140.0;3449.;10.5;70;US\n", + "Ford Galaxie 500;15.0;8;429.0;198.0;4341.;10.0;70;US\n", + "Chevrolet Impala;14.0;8;454.0;220.0;4354.;9.0;70;US\n", + "Plymouth Fury iii;14.0;8;440.0;215.0;4312.;8.5;70;US\n", + "Pontiac Catalina;14.0;8;455.0;225.0;4425.;10.0;70;US\n", + "AMC Ambassador DPL;15.0;8;390.0;190.0;3850.;8.5;70;US\n", + "Citroen DS-21 Pallas;0;4;133.0;115.0;3090.;17.5;70;Europe\n", + "Chevrolet Chevelle Concours (sw);0;8;350.0;165.0;4142.;11.5;70;US\n", + "Ford Torino (sw);0;8;351.0;153.0;4034.;11.0;70;US\n", + "Plymouth Satellite (sw);0;8;383.0;175.0;4166.;10.5;70;US\n", + "AMC Rebel SST (sw);0;8;360.0;175.0;3850.;11.0;70;US\n", + "Dodge Challenger SE;15.0;8;383.0;170.0;3563.;10.0;70;US\n", + "Plymouth 'Cuda 340;14.0;8;340.0;160.0;3609.;8.0;70;US\n", + "Ford Mustang Boss 302;0;8;302.0;140.0;3353.;8.0;70;US\n", + "Chevrolet Monte Carlo;15.0;8;400.0;150.0;3761.;9.5;70;US\n", + "Buick Estate Wagon (sw);14.0;8;455.0;225.0;3086.;10.0;70;US\n", + "Toyota Corolla Mark ii;24.0;4;113.0;95.00;2372.;15.0;70;Japan\n", + "Plymouth Duster;22.0;6;198.0;95.00;2833.;15.5;70;US\n", + "AMC Hornet;18.0;6;199.0;97.00;2774.;15.5;70;US\n", + "Ford Maverick;21.0;6;200.0;85.00;2587.;16.0;70;US\n", + "Datsun PL510;27.0;4;97.00;88.00;2130.;14.5;70;Japan\n", + "Volkswagen 1131 Deluxe Sedan;26.0;4;97.00;46.00;1835.;20.5;70;Europe\n", + "Peugeot 504;25.0;4;110.0;87.00;2672.;17.5;70;Europe\n", + "Audi 100 LS;24.0;4;107.0;90.00;2430.;14.5;70;Europe\n", + "Saab 99e;25.0;4;104.0;95.00;2375.;17.5;70;Europe\n", + "BMW 2002;26.0;4;121.0;113.0;2234.;12.5;70;Europe\n", + "AMC Gremlin;21.0;6;199.0;90.00;2648.;15.0;70;US\n", + "Ford F250;10.0;8;360.0;215.0;4615.;14.0;70;US\n", + "Chevy C20;10.0;8;307.0;200.0;4376.;15.0;70;US\n", + "Dodge D200;11.0;8;318.0;210.0;4382.;13.5;70;US\n", + "Hi 1200D;9.0;8;304.0;193.0;4732.;18.5;70;US\n", + "Datsun PL510;27.0;4;97.00;88.00;2130.;14.5;71;Japan\n", + "Chevrolet Vega 2300;28.0;4;140.0;90.00;2264.;15.5;71;US\n", + "Toyota Corolla;25.0;4;113.0;95.00;2228.;14.0;71;Japan\n", + "Ford Pinto;25.0;4;98.00;0;2046.;19.0;71;US\n", + "Volkswagen Super Beetle 117;0;4;97.00;48.00;1978.;20.0;71;Europe\n", + "AMC Gremlin;19.0;6;232.0;100.0;2634.;13.0;71;US\n", + "Plymouth Satellite Custom;16.0;6;225.0;105.0;3439.;15.5;71;US\n", + "Chevrolet Chevelle Malibu;17.0;6;250.0;100.0;3329.;15.5;71;US\n", + "Ford Torino 500;19.0;6;250.0;88.00;3302.;15.5;71;US\n", + "AMC Matador;18.0;6;232.0;100.0;3288.;15.5;71;US\n", + "Chevrolet Impala;14.0;8;350.0;165.0;4209.;12.0;71;US\n", + "Pontiac Catalina Brougham;14.0;8;400.0;175.0;4464.;11.5;71;US\n", + "Ford Galaxie 500;14.0;8;351.0;153.0;4154.;13.5;71;US\n", + "Plymouth Fury iii;14.0;8;318.0;150.0;4096.;13.0;71;US\n", + "Dodge Monaco (sw);12.0;8;383.0;180.0;4955.;11.5;71;US\n", + "Ford Country Squire (sw);13.0;8;400.0;170.0;4746.;12.0;71;US\n", + "Pontiac Safari (sw);13.0;8;400.0;175.0;5140.;12.0;71;US\n", + "AMC Hornet Sportabout (sw);18.0;6;258.0;110.0;2962.;13.5;71;US\n", + "Chevrolet Vega (sw);22.0;4;140.0;72.00;2408.;19.0;71;US\n", + "Pontiac Firebird;19.0;6;250.0;100.0;3282.;15.0;71;US\n", + "Ford Mustang;18.0;6;250.0;88.00;3139.;14.5;71;US\n", + "Mercury Capri 2000;23.0;4;122.0;86.00;2220.;14.0;71;US\n", + "Opel 1900;28.0;4;116.0;90.00;2123.;14.0;71;Europe\n", + "Peugeot 304;30.0;4;79.00;70.00;2074.;19.5;71;Europe\n", + "Fiat 124B;30.0;4;88.00;76.00;2065.;14.5;71;Europe\n", + "Toyota Corolla 1200;31.0;4;71.00;65.00;1773.;19.0;71;Japan\n", + "Datsun 1200;35.0;4;72.00;69.00;1613.;18.0;71;Japan\n", + "Volkswagen Model 111;27.0;4;97.00;60.00;1834.;19.0;71;Europe\n", + "Plymouth Cricket;26.0;4;91.00;70.00;1955.;20.5;71;US\n", + "Toyota Corolla Hardtop;24.0;4;113.0;95.00;2278.;15.5;72;Japan\n", + "Dodge Colt Hardtop;25.0;4;97.50;80.00;2126.;17.0;72;US\n", + "Volkswagen Type 3;23.0;4;97.00;54.00;2254.;23.5;72;Europe\n", + "Chevrolet Vega;20.0;4;140.0;90.00;2408.;19.5;72;US\n", + "Ford Pinto Runabout;21.0;4;122.0;86.00;2226.;16.5;72;US\n", + "Chevrolet Impala;13.0;8;350.0;165.0;4274.;12.0;72;US\n", + "Pontiac Catalina;14.0;8;400.0;175.0;4385.;12.0;72;US\n", + "Plymouth Fury III;15.0;8;318.0;150.0;4135.;13.5;72;US\n", + "Ford Galaxie 500;14.0;8;351.0;153.0;4129.;13.0;72;US\n", + "AMC Ambassador SST;17.0;8;304.0;150.0;3672.;11.5;72;US\n", + "Mercury Marquis;11.0;8;429.0;208.0;4633.;11.0;72;US\n", + "Buick LeSabre Custom;13.0;8;350.0;155.0;4502.;13.5;72;US\n", + "Oldsmobile Delta 88 Royale;12.0;8;350.0;160.0;4456.;13.5;72;US\n", + "Chrysler Newport Royal;13.0;8;400.0;190.0;4422.;12.5;72;US\n", + "Mazda RX2 Coupe;19.0;3;70.00;97.00;2330.;13.5;72;Japan\n", + "AMC Matador (sw);15.0;8;304.0;150.0;3892.;12.5;72;US\n", + "Chevrolet Chevelle Concours (sw);13.0;8;307.0;130.0;4098.;14.0;72;US\n", + "Ford Gran Torino (sw);13.0;8;302.0;140.0;4294.;16.0;72;US\n", + "Plymouth Satellite Custom (sw);14.0;8;318.0;150.0;4077.;14.0;72;US\n", + "Volvo 145e (sw);18.0;4;121.0;112.0;2933.;14.5;72;Europe\n", + "Volkswagen 411 (sw);22.0;4;121.0;76.00;2511.;18.0;72;Europe\n", + "Peugeot 504 (sw);21.0;4;120.0;87.00;2979.;19.5;72;Europe\n", + "Renault 12 (sw);26.0;4;96.00;69.00;2189.;18.0;72;Europe\n", + "Ford Pinto (sw);22.0;4;122.0;86.00;2395.;16.0;72;US\n", + "Datsun 510 (sw);28.0;4;97.00;92.00;2288.;17.0;72;Japan\n", + "Toyota Corolla Mark II (sw);23.0;4;120.0;97.00;2506.;14.5;72;Japan\n", + "Dodge Colt (sw);28.0;4;98.00;80.00;2164.;15.0;72;US\n", + "Toyota Corolla 1600 (sw);27.0;4;97.00;88.00;2100.;16.5;72;Japan\n", + "Buick Century 350;13.0;8;350.0;175.0;4100.;13.0;73;US\n", + "AMC Matador;14.0;8;304.0;150.0;3672.;11.5;73;US\n", + "Chevrolet Malibu;13.0;8;350.0;145.0;3988.;13.0;73;US\n", + "Ford Gran Torino;14.0;8;302.0;137.0;4042.;14.5;73;US\n", + "Dodge Coronet Custom;15.0;8;318.0;150.0;3777.;12.5;73;US\n", + "Mercury Marquis Brougham;12.0;8;429.0;198.0;4952.;11.5;73;US\n", + "Chevrolet Caprice Classic;13.0;8;400.0;150.0;4464.;12.0;73;US\n", + "Ford LTD;13.0;8;351.0;158.0;4363.;13.0;73;US\n", + "Plymouth Fury Gran Sedan;14.0;8;318.0;150.0;4237.;14.5;73;US\n", + "Chrysler New Yorker Brougham;13.0;8;440.0;215.0;4735.;11.0;73;US\n", + "Buick Electra 225 Custom;12.0;8;455.0;225.0;4951.;11.0;73;US\n", + "AMC Ambassador Brougham;13.0;8;360.0;175.0;3821.;11.0;73;US\n", + "Plymouth Valiant;18.0;6;225.0;105.0;3121.;16.5;73;US\n", + "Chevrolet Nova Custom;16.0;6;250.0;100.0;3278.;18.0;73;US\n", + "AMC Hornet;18.0;6;232.0;100.0;2945.;16.0;73;US\n", + "Ford Maverick;18.0;6;250.0;88.00;3021.;16.5;73;US\n", + "Plymouth Duster;23.0;6;198.0;95.00;2904.;16.0;73;US\n", + "Volkswagen Super Beetle;26.0;4;97.00;46.00;1950.;21.0;73;Europe\n", + "Chevrolet Impala;11.0;8;400.0;150.0;4997.;14.0;73;US\n", + "Ford Country;12.0;8;400.0;167.0;4906.;12.5;73;US\n", + "Plymouth Custom Suburb;13.0;8;360.0;170.0;4654.;13.0;73;US\n", + "Oldsmobile Vista Cruiser;12.0;8;350.0;180.0;4499.;12.5;73;US\n", + "AMC Gremlin;18.0;6;232.0;100.0;2789.;15.0;73;US\n", + "Toyota Camry;20.0;4;97.00;88.00;2279.;19.0;73;Japan\n", + "Chevrolet Vega;21.0;4;140.0;72.00;2401.;19.5;73;US\n", + "Datsun 610;22.0;4;108.0;94.00;2379.;16.5;73;Japan\n", + "Mazda RX3;18.0;3;70.00;90.00;2124.;13.5;73;Japan\n", + "Ford Pinto;19.0;4;122.0;85.00;2310.;18.5;73;US\n", + "Mercury Capri v6;21.0;6;155.0;107.0;2472.;14.0;73;US\n", + "Fiat 124 Sport Coupe;26.0;4;98.00;90.00;2265.;15.5;73;Europe\n", + "Chevrolet Monte Carlo S;15.0;8;350.0;145.0;4082.;13.0;73;US\n", + "Pontiac Grand Prix;16.0;8;400.0;230.0;4278.;9.50;73;US\n", + "Fiat 128;29.0;4;68.00;49.00;1867.;19.5;73;Europe\n", + "Opel Manta;24.0;4;116.0;75.00;2158.;15.5;73;Europe\n", + "Audi 100LS;20.0;4;114.0;91.00;2582.;14.0;73;Europe\n", + "Volvo 144ea;19.0;4;121.0;112.0;2868.;15.5;73;Europe\n", + "Dodge Dart Custom;15.0;8;318.0;150.0;3399.;11.0;73;US\n", + "Saab 99le;24.0;4;121.0;110.0;2660.;14.0;73;Europe\n", + "Toyota Mark II;20.0;6;156.0;122.0;2807.;13.5;73;Japan\n", + "Oldsmobile Omega;11.0;8;350.0;180.0;3664.;11.0;73;US\n", + "Plymouth Duster;20.0;6;198.0;95.00;3102.;16.5;74;US\n", + "Ford Maverick;21.0;6;200.0;0;2875.;17.0;74;US\n", + "AMC Hornet;19.0;6;232.0;100.0;2901.;16.0;74;US\n", + "Chevrolet Nova;15.0;6;250.0;100.0;3336.;17.0;74;US\n", + "Datsun B210;31.0;4;79.00;67.00;1950.;19.0;74;Japan\n", + "Ford Pinto;26.0;4;122.0;80.00;2451.;16.5;74;US\n", + "Toyota Corolla 1200;32.0;4;71.00;65.00;1836.;21.0;74;Japan\n", + "Chevrolet Vega;25.0;4;140.0;75.00;2542.;17.0;74;US\n", + "Chevrolet Chevelle Malibu Classic;16.0;6;250.0;100.0;3781.;17.0;74;US\n", + "AMC Matador;16.0;6;258.0;110.0;3632.;18.0;74;US\n", + "Plymouth Satellite Sebring;18.0;6;225.0;105.0;3613.;16.5;74;US\n", + "Ford Gran Torino;16.0;8;302.0;140.0;4141.;14.0;74;US\n", + "Buick Century Luxus (sw);13.0;8;350.0;150.0;4699.;14.5;74;US\n", + "Dodge Coronet Custom (sw);14.0;8;318.0;150.0;4457.;13.5;74;US\n", + "Ford Gran Torino (sw);14.0;8;302.0;140.0;4638.;16.0;74;US\n", + "AMC Matador (sw);14.0;8;304.0;150.0;4257.;15.5;74;US\n", + "Audi Fox;29.0;4;98.00;83.00;2219.;16.5;74;Europe\n", + "Volkswagen Dasher;26.0;4;79.00;67.00;1963.;15.5;74;Europe\n", + "Opel Manta;26.0;4;97.00;78.00;2300.;14.5;74;Europe\n", + "Toyota Corolla;31.0;4;76.00;52.00;1649.;16.5;74;Japan\n", + "Datsun 710;32.0;4;83.00;61.00;2003.;19.0;74;Japan\n", + "Dodge Colt;28.0;4;90.00;75.00;2125.;14.5;74;US\n", + "Fiat 128;24.0;4;90.00;75.00;2108.;15.5;74;Europe\n", + "Fiat 124 TC;26.0;4;116.0;75.00;2246.;14.0;74;Europe\n", + "Honda Civic;24.0;4;120.0;97.00;2489.;15.0;74;Japan\n", + "Subaru;26.0;4;108.0;93.00;2391.;15.5;74;Japan\n", + "Fiat x1.9;31.0;4;79.00;67.00;2000.;16.0;74;Europe\n", + "Plymouth Valiant Custom;19.0;6;225.0;95.00;3264.;16.0;75;US\n", + "Chevrolet Nova;18.0;6;250.0;105.0;3459.;16.0;75;US\n", + "Mercury Monarch;15.0;6;250.0;72.00;3432.;21.0;75;US\n", + "Ford Maverick;15.0;6;250.0;72.00;3158.;19.5;75;US\n", + "Pontiac Catalina;16.0;8;400.0;170.0;4668.;11.5;75;US\n", + "Chevrolet Bel Air;15.0;8;350.0;145.0;4440.;14.0;75;US\n", + "Plymouth Grand Fury;16.0;8;318.0;150.0;4498.;14.5;75;US\n", + "Ford LTD;14.0;8;351.0;148.0;4657.;13.5;75;US\n", + "Buick Century;17.0;6;231.0;110.0;3907.;21.0;75;US\n", + "Chevrolete Chevelle Malibu;16.0;6;250.0;105.0;3897.;18.5;75;US\n", + "AMC Matador;15.0;6;258.0;110.0;3730.;19.0;75;US\n", + "Plymouth Fury;18.0;6;225.0;95.00;3785.;19.0;75;US\n", + "Buick Skyhawk;21.0;6;231.0;110.0;3039.;15.0;75;US\n", + "Chevrolet Monza 2+2;20.0;8;262.0;110.0;3221.;13.5;75;US\n", + "Ford Mustang II;13.0;8;302.0;129.0;3169.;12.0;75;US\n", + "Toyota Corolla;29.0;4;97.00;75.00;2171.;16.0;75;Japan\n", + "Ford Pinto;23.0;4;140.0;83.00;2639.;17.0;75;US\n", + "AMC Gremlin;20.0;6;232.0;100.0;2914.;16.0;75;US\n", + "Pontiac Astro;23.0;4;140.0;78.00;2592.;18.5;75;US\n", + "Toyota Corolla;24.0;4;134.0;96.00;2702.;13.5;75;Japan\n", + "Volkswagen Dasher;25.0;4;90.00;71.00;2223.;16.5;75;Europe\n", + "Datsun 710;24.0;4;119.0;97.00;2545.;17.0;75;Japan\n", + "Ford Pinto;18.0;6;171.0;97.00;2984.;14.5;75;US\n", + "Volkswagen Rabbit;29.0;4;90.00;70.00;1937.;14.0;75;Europe\n", + "AMC Pacer;19.0;6;232.0;90.00;3211.;17.0;75;US\n", + "Audi 100LS;23.0;4;115.0;95.00;2694.;15.0;75;Europe\n", + "Peugeot 504;23.0;4;120.0;88.00;2957.;17.0;75;Europe\n", + "Volvo 244DL;22.0;4;121.0;98.00;2945.;14.5;75;Europe\n", + "Saab 99LE;25.0;4;121.0;115.0;2671.;13.5;75;Europe\n", + "Honda Civic CVCC;33.0;4;91.00;53.00;1795.;17.5;75;Japan\n", + "Fiat 131;28.0;4;107.0;86.00;2464.;15.5;76;Europe\n", + "Opel 1900;25.0;4;116.0;81.00;2220.;16.9;76;Europe\n", + "Capri ii;25.0;4;140.0;92.00;2572.;14.9;76;US\n", + "Dodge Colt;26.0;4;98.00;79.00;2255.;17.7;76;US\n", + "Renault 12tl;27.0;4;101.0;83.00;2202.;15.3;76;Europe\n", + "Chevrolet Chevelle Malibu Classic;17.5;8;305.0;140.0;4215.;13.0;76;US\n", + "Dodge Coronet Brougham;16.0;8;318.0;150.0;4190.;13.0;76;US\n", + "AMC Matador;15.5;8;304.0;120.0;3962.;13.9;76;US\n", + "Ford Gran Torino;14.5;8;351.0;152.0;4215.;12.8;76;US\n", + "Plymouth Valiant;22.0;6;225.0;100.0;3233.;15.4;76;US\n", + "Chevrolet Nova;22.0;6;250.0;105.0;3353.;14.5;76;US\n", + "Ford Maverick;24.0;6;200.0;81.00;3012.;17.6;76;US\n", + "AMC Hornet;22.5;6;232.0;90.00;3085.;17.6;76;US\n", + "Chevrolet Chevette;29.0;4;85.00;52.00;2035.;22.2;76;US\n", + "Chevrolet Woody;24.5;4;98.00;60.00;2164.;22.1;76;US\n", + "Volkswagen Rabbit;29.0;4;90.00;70.00;1937.;14.2;76;Europe\n", + "Honda Civic;33.0;4;91.00;53.00;1795.;17.4;76;Japan\n", + "Dodge Aspen SE;20.0;6;225.0;100.0;3651.;17.7;76;US\n", + "Ford Grenada ghia;18.0;6;250.0;78.00;3574.;21.0;76;US\n", + "Pontiac Ventura SJ;18.5;6;250.0;110.0;3645.;16.2;76;US\n", + "AMC Pacer d/l;17.5;6;258.0;95.00;3193.;17.8;76;US\n", + "Volkswagen Rabbit;29.5;4;97.00;71.00;1825.;12.2;76;Europe\n", + "Datsun B-210;32.0;4;85.00;70.00;1990.;17.0;76;Japan\n", + "Toyota Corolla;28.0;4;97.00;75.00;2155.;16.4;76;Japan\n", + "Ford Pinto;26.5;4;140.0;72.00;2565.;13.6;76;US\n", + "Volvo 245;20.0;4;130.0;102.0;3150.;15.7;76;Europe\n", + "Plymouth Volare Premier v8;13.0;8;318.0;150.0;3940.;13.2;76;US\n", + "Peugeot 504;19.0;4;120.0;88.00;3270.;21.9;76;Europe\n", + "Toyota Mark II;19.0;6;156.0;108.0;2930.;15.5;76;Japan\n", + "Mercedes-Benz 280s;16.5;6;168.0;120.0;3820.;16.7;76;Europe\n", + "Cadillac Seville;16.5;8;350.0;180.0;4380.;12.1;76;US\n", + "Chevrolet C10;13.0;8;350.0;145.0;4055.;12.0;76;US\n", + "Ford F108;13.0;8;302.0;130.0;3870.;15.0;76;US\n", + "Dodge D100;13.0;8;318.0;150.0;3755.;14.0;76;US\n", + "Honda Accord CVCC;31.5;4;98.00;68.00;2045.;18.5;77;Japan\n", + "Buick Opel Isuzu Deluxe;30.0;4;111.0;80.00;2155.;14.8;77;US\n", + "Renault 5 GTL;36.0;4;79.00;58.00;1825.;18.6;77;Europe\n", + "Plymouth Arrow GS;25.5;4;122.0;96.00;2300.;15.5;77;US\n", + "Datsun F-10 Hatchback;33.5;4;85.00;70.00;1945.;16.8;77;Japan\n", + "Chevrolet Caprice Classic;17.5;8;305.0;145.0;3880.;12.5;77;US\n", + "Oldsmobile Cutlass Supreme;17.0;8;260.0;110.0;4060.;19.0;77;US\n", + "Dodge Monaco Brougham;15.5;8;318.0;145.0;4140.;13.7;77;US\n", + "Mercury Cougar Brougham;15.0;8;302.0;130.0;4295.;14.9;77;US\n", + "Chevrolet Concours;17.5;6;250.0;110.0;3520.;16.4;77;US\n", + "Buick Skylark;20.5;6;231.0;105.0;3425.;16.9;77;US\n", + "Plymouth Volare Custom;19.0;6;225.0;100.0;3630.;17.7;77;US\n", + "Ford Grenada;18.5;6;250.0;98.00;3525.;19.0;77;US\n", + "Pontiac Grand Prix LJ;16.0;8;400.0;180.0;4220.;11.1;77;US\n", + "Chevrolet Monte Carlo Landau;15.5;8;350.0;170.0;4165.;11.4;77;US\n", + "Chrysler Cordoba;15.5;8;400.0;190.0;4325.;12.2;77;US\n", + "Ford Thunderbird;16.0;8;351.0;149.0;4335.;14.5;77;US\n", + "Volkswagen Rabbit Custom;29.0;4;97.00;78.00;1940.;14.5;77;Europe\n", + "Pontiac Sunbird Coupe;24.5;4;151.0;88.00;2740.;16.0;77;US\n", + "Toyota Corolla Liftback;26.0;4;97.00;75.00;2265.;18.2;77;Japan\n", + "Ford Mustang II 2+2;25.5;4;140.0;89.00;2755.;15.8;77;US\n", + "Chevrolet Chevette;30.5;4;98.00;63.00;2051.;17.0;77;US\n", + "Dodge Colt m/m;33.5;4;98.00;83.00;2075.;15.9;77;US\n", + "Subaru DL;30.0;4;97.00;67.00;1985.;16.4;77;Japan\n", + "Volkswagen Dasher;30.5;4;97.00;78.00;2190.;14.1;77;Europe\n", + "Datsun 810;22.0;6;146.0;97.00;2815.;14.5;77;Japan\n", + "BMW 320i;21.5;4;121.0;110.0;2600.;12.8;77;Europe\n", + "Mazda RX-4;21.5;3;80.00;110.0;2720.;13.5;77;Japan\n", + "Volkswagen Rabbit Custom Diesel;43.1;4;90.00;48.00;1985.;21.5;78;Europe\n", + "Ford Fiesta;36.1;4;98.00;66.00;1800.;14.4;78;US\n", + "Mazda GLC Deluxe;32.8;4;78.00;52.00;1985.;19.4;78;Japan\n", + "Datsun B210 GX;39.4;4;85.00;70.00;2070.;18.6;78;Japan\n", + "Honda Civic CVCC;36.1;4;91.00;60.00;1800.;16.4;78;Japan\n", + "Oldsmobile Cutlass Salon Brougham;19.9;8;260.0;110.0;3365.;15.5;78;US\n", + "Dodge Diplomat;19.4;8;318.0;140.0;3735.;13.2;78;US\n", + "Mercury Monarch ghia;20.2;8;302.0;139.0;3570.;12.8;78;US\n", + "Pontiac Phoenix LJ;19.2;6;231.0;105.0;3535.;19.2;78;US\n", + "Chevrolet Malibu;20.5;6;200.0;95.00;3155.;18.2;78;US\n", + "Ford Fairmont (auto);20.2;6;200.0;85.00;2965.;15.8;78;US\n", + "Ford Fairmont (man);25.1;4;140.0;88.00;2720.;15.4;78;US\n", + "Plymouth Volare;20.5;6;225.0;100.0;3430.;17.2;78;US\n", + "AMC Concord;19.4;6;232.0;90.00;3210.;17.2;78;US\n", + "Buick Century Special;20.6;6;231.0;105.0;3380.;15.8;78;US\n", + "Mercury Zephyr;20.8;6;200.0;85.00;3070.;16.7;78;US\n", + "Dodge Aspen;18.6;6;225.0;110.0;3620.;18.7;78;US\n", + "AMC Concord d/l;18.1;6;258.0;120.0;3410.;15.1;78;US\n", + "Chevrolet Monte Carlo Landau;19.2;8;305.0;145.0;3425.;13.2;78;US\n", + "Buick Regal Sport Coupe (turbo);17.7;6;231.0;165.0;3445.;13.4;78;US\n", + "Ford Futura;18.1;8;302.0;139.0;3205.;11.2;78;US\n", + "Dodge Magnum XE;17.5;8;318.0;140.0;4080.;13.7;78;US\n", + "Chevrolet Chevette;30.0;4;98.00;68.00;2155.;16.5;78;US\n", + "Toyota Corolla;27.5;4;134.0;95.00;2560.;14.2;78;Japan\n", + "Datsun 510;27.2;4;119.0;97.00;2300.;14.7;78;Japan\n", + "Dodge Omni;30.9;4;105.0;75.00;2230.;14.5;78;US\n", + "Toyota Celica GT Liftback;21.1;4;134.0;95.00;2515.;14.8;78;Japan\n", + "Plymouth Sapporo;23.2;4;156.0;105.0;2745.;16.7;78;US\n", + "Oldsmobile Starfire SX;23.8;4;151.0;85.00;2855.;17.6;78;US\n", + "Datsun 200-SX;23.9;4;119.0;97.00;2405.;14.9;78;Japan\n", + "Audi 5000;20.3;5;131.0;103.0;2830.;15.9;78;Europe\n", + "Volvo 264gl;17.0;6;163.0;125.0;3140.;13.6;78;Europe\n", + "Saab 99gle;21.6;4;121.0;115.0;2795.;15.7;78;Europe\n", + "Peugeot 604sl;16.2;6;163.0;133.0;3410.;15.8;78;Europe\n", + "Volkswagen Scirocco;31.5;4;89.00;71.00;1990.;14.9;78;Europe\n", + "Honda Accord LX;29.5;4;98.00;68.00;2135.;16.6;78;Japan\n", + "Pontiac Lemans V6;21.5;6;231.0;115.0;3245.;15.4;79;US\n", + "Mercury Zephyr 6;19.8;6;200.0;85.00;2990.;18.2;79;US\n", + "Ford Fairmont 4;22.3;4;140.0;88.00;2890.;17.3;79;US\n", + "AMC Concord DL 6;20.2;6;232.0;90.00;3265.;18.2;79;US\n", + "Dodge Aspen 6;20.6;6;225.0;110.0;3360.;16.6;79;US\n", + "Chevrolet Caprice Classic;17.0;8;305.0;130.0;3840.;15.4;79;US\n", + "Ford LTD Landau;17.6;8;302.0;129.0;3725.;13.4;79;US\n", + "Mercury Grand Marquis;16.5;8;351.0;138.0;3955.;13.2;79;US\n", + "Dodge St. Regis;18.2;8;318.0;135.0;3830.;15.2;79;US\n", + "Buick Estate Wagon (sw);16.9;8;350.0;155.0;4360.;14.9;79;US\n", + "Ford Country Squire (sw);15.5;8;351.0;142.0;4054.;14.3;79;US\n", + "Chevrolet Malibu Classic (sw);19.2;8;267.0;125.0;3605.;15.0;79;US\n", + "Chrysler Lebaron Town @ Country (sw);18.5;8;360.0;150.0;3940.;13.0;79;US\n", + "Volkswagen Rabbit Custom;31.9;4;89.00;71.00;1925.;14.0;79;Europe\n", + "Mazda GLC Deluxe;34.1;4;86.00;65.00;1975.;15.2;79;Japan\n", + "Dodge Colt Hatchback Custom;35.7;4;98.00;80.00;1915.;14.4;79;US\n", + "AMC Spirit DL;27.4;4;121.0;80.00;2670.;15.0;79;US\n", + "Mercedes Benz 300d;25.4;5;183.0;77.00;3530.;20.1;79;Europe\n", + "Cadillac Eldorado;23.0;8;350.0;125.0;3900.;17.4;79;US\n", + "Peugeot 504;27.2;4;141.0;71.00;3190.;24.8;79;Europe\n", + "Oldsmobile Cutlass Salon Brougham;23.9;8;260.0;90.00;3420.;22.2;79;US\n", + "Plymouth Horizon;34.2;4;105.0;70.00;2200.;13.2;79;US\n", + "Plymouth Horizon TC3;34.5;4;105.0;70.00;2150.;14.9;79;US\n", + "Datsun 210;31.8;4;85.00;65.00;2020.;19.2;79;Japan\n", + "Fiat Strada Custom;37.3;4;91.00;69.00;2130.;14.7;79;Europe\n", + "Buick Skylark Limited;28.4;4;151.0;90.00;2670.;16.0;79;US\n", + "Chevrolet Citation;28.8;6;173.0;115.0;2595.;11.3;79;US\n", + "Oldsmobile Omega Brougham;26.8;6;173.0;115.0;2700.;12.9;79;US\n", + "Pontiac Phoenix;33.5;4;151.0;90.00;2556.;13.2;79;US\n", + "Volkswagen Rabbit;41.5;4;98.00;76.00;2144.;14.7;80;Europe\n", + "Toyota Corolla Tercel;38.1;4;89.00;60.00;1968.;18.8;80;Japan\n", + "Chevrolet Chevette;32.1;4;98.00;70.00;2120.;15.5;80;US\n", + "Datsun 310;37.2;4;86.00;65.00;2019.;16.4;80;Japan\n", + "Chevrolet Citation;28.0;4;151.0;90.00;2678.;16.5;80;US\n", + "Ford Fairmont;26.4;4;140.0;88.00;2870.;18.1;80;US\n", + "AMC Concord;24.3;4;151.0;90.00;3003.;20.1;80;US\n", + "Dodge Aspen;19.1;6;225.0;90.00;3381.;18.7;80;US\n", + "Audi 4000;34.3;4;97.00;78.00;2188.;15.8;80;Europe\n", + "Toyota Corolla Liftback;29.8;4;134.0;90.00;2711.;15.5;80;Japan\n", + "Mazda 626;31.3;4;120.0;75.00;2542.;17.5;80;Japan\n", + "Datsun 510 Hatchback;37.0;4;119.0;92.00;2434.;15.0;80;Japan\n", + "Toyota Corolla;32.2;4;108.0;75.00;2265.;15.2;80;Japan\n", + "Mazda GLC;46.6;4;86.00;65.00;2110.;17.9;80;Japan\n", + "Dodge Colt;27.9;4;156.0;105.0;2800.;14.4;80;US\n", + "Datsun 210;40.8;4;85.00;65.00;2110.;19.2;80;Japan\n", + "Volkswagen Rabbit C (Diesel);44.3;4;90.00;48.00;2085.;21.7;80;Europe\n", + "Volkswagen Dasher (diesel);43.4;4;90.00;48.00;2335.;23.7;80;Europe\n", + "Audi 5000s (diesel);36.4;5;121.0;67.00;2950.;19.9;80;Europe\n", + "Mercedes-Benz 240d;30.0;4;146.0;67.00;3250.;21.8;80;Europe\n", + "Honda Civic 1500 gl;44.6;4;91.00;67.00;1850.;13.8;80;Japan\n", + "Renault Lecar Deluxe;40.9;4;85.00;0;1835.;17.3;80;Europe\n", + "Subaru DL;33.8;4;97.00;67.00;2145.;18.0;80;Japan\n", + "Volkswagen Rabbit;29.8;4;89.00;62.00;1845.;15.3;80;Europe\n", + "Datsun 280-ZX;32.7;6;168.0;132.0;2910.;11.4;80;Japan\n", + "Mazda RX-7 GS;23.7;3;70.00;100.0;2420.;12.5;80;Japan\n", + "Triumph TR7 Coupe;35.0;4;122.0;88.00;2500.;15.1;80;Europe\n", + "Ford Mustang Cobra;23.6;4;140.0;0;2905.;14.3;80;US\n", + "Honda Accord;32.4;4;107.0;72.00;2290.;17.0;80;Japan\n", + "Plymouth Reliant;27.2;4;135.0;84.00;2490.;15.7;81;US\n", + "Buick Skylark;26.6;4;151.0;84.00;2635.;16.4;81;US\n", + "Dodge Aries Wagon (sw);25.8;4;156.0;92.00;2620.;14.4;81;US\n", + "Chevrolet Citation;23.5;6;173.0;110.0;2725.;12.6;81;US\n", + "Plymouth Reliant;30.0;4;135.0;84.00;2385.;12.9;81;US\n", + "Toyota Starlet;39.1;4;79.00;58.00;1755.;16.9;81;Japan\n", + "Plymouth Champ;39.0;4;86.00;64.00;1875.;16.4;81;US\n", + "Honda Civic 1300;35.1;4;81.00;60.00;1760.;16.1;81;Japan\n", + "Subaru;32.3;4;97.00;67.00;2065.;17.8;81;Japan\n", + "Datsun 210 MPG;37.0;4;85.00;65.00;1975.;19.4;81;Japan\n", + "Toyota Tercel;37.7;4;89.00;62.00;2050.;17.3;81;Japan\n", + "Mazda GLC 4;34.1;4;91.00;68.00;1985.;16.0;81;Japan\n", + "Plymouth Horizon 4;34.7;4;105.0;63.00;2215.;14.9;81;US\n", + "Ford Escort 4W;34.4;4;98.00;65.00;2045.;16.2;81;US\n", + "Ford Escort 2H;29.9;4;98.00;65.00;2380.;20.7;81;US\n", + "Volkswagen Jetta;33.0;4;105.0;74.00;2190.;14.2;81;Europe\n", + "Renault 18i;34.5;4;100.0;0;2320.;15.8;81;Europe\n", + "Honda Prelude;33.7;4;107.0;75.00;2210.;14.4;81;Japan\n", + "Toyota Corolla;32.4;4;108.0;75.00;2350.;16.8;81;Japan\n", + "Datsun 200SX;32.9;4;119.0;100.0;2615.;14.8;81;Japan\n", + "Mazda 626;31.6;4;120.0;74.00;2635.;18.3;81;Japan\n", + "Peugeot 505s Turbo Diesel;28.1;4;141.0;80.00;3230.;20.4;81;Europe\n", + "Saab 900s;0;4;121.0;110.0;2800.;15.4;81;Europe\n", + "Volvo Diesel;30.7;6;145.0;76.00;3160.;19.6;81;Europe\n", + "Toyota Cressida;25.4;6;168.0;116.0;2900.;12.6;81;Japan\n", + "Datsun 810 Maxima;24.2;6;146.0;120.0;2930.;13.8;81;Japan\n", + "Buick Century;22.4;6;231.0;110.0;3415.;15.8;81;US\n", + "Oldsmobile Cutlass LS;26.6;8;350.0;105.0;3725.;19.0;81;US\n", + "Ford Grenada gl;20.2;6;200.0;88.00;3060.;17.1;81;US\n", + "Chrysler Lebaron Salon;17.6;6;225.0;85.00;3465.;16.6;81;US\n", + "Chevrolet Cavalier;28.0;4;112.0;88.00;2605.;19.6;82;US\n", + "Chevrolet Cavalier Wagon;27.0;4;112.0;88.00;2640.;18.6;82;US\n", + "Chevrolet Cavalier 2-door;34.0;4;112.0;88.00;2395.;18.0;82;US\n", + "Pontiac J2000 SE Hatchback;31.0;4;112.0;85.00;2575.;16.2;82;US\n", + "Dodge Aries SE;29.0;4;135.0;84.00;2525.;16.0;82;US\n", + "Pontiac Phoenix;27.0;4;151.0;90.00;2735.;18.0;82;US\n", + "Ford Fairmont Futura;24.0;4;140.0;92.00;2865.;16.4;82;US\n", + "AMC Concord DL;23.0;4;151.0;0;3035.;20.5;82;US\n", + "Volkswagen Rabbit l;36.0;4;105.0;74.00;1980.;15.3;82;Europe\n", + "Mazda GLC Custom l;37.0;4;91.00;68.00;2025.;18.2;82;Japan\n", + "Mazda GLC Custom;31.0;4;91.00;68.00;1970.;17.6;82;Japan\n", + "Plymouth Horizon Miser;38.0;4;105.0;63.00;2125.;14.7;82;US\n", + "Mercury Lynx l;36.0;4;98.00;70.00;2125.;17.3;82;US\n", + "Nissan Stanza XE;36.0;4;120.0;88.00;2160.;14.5;82;Japan\n", + "Honda Accord;36.0;4;107.0;75.00;2205.;14.5;82;Japan\n", + "Toyota Corolla;34.0;4;108.0;70.00;2245;16.9;82;Japan\n", + "Honda Civic;38.0;4;91.00;67.00;1965.;15.0;82;Japan\n", + "Honda Civic (auto);32.0;4;91.00;67.00;1965.;15.7;82;Japan\n", + "Datsun 310 GX;38.0;4;91.00;67.00;1995.;16.2;82;Japan\n", + "Buick Century Limited;25.0;6;181.0;110.0;2945.;16.4;82;US\n", + "Oldsmobile Cutlass Ciera (diesel);38.0;6;262.0;85.00;3015.;17.0;82;US\n", + "Chrysler Lebaron Medallion;26.0;4;156.0;92.00;2585.;14.5;82;US\n", + "Ford Grenada l;22.0;6;232.0;112.0;2835;14.7;82;US\n", + "Toyota Celica GT;32.0;4;144.0;96.00;2665.;13.9;82;Japan\n", + "Dodge Charger 2.2;36.0;4;135.0;84.00;2370.;13.0;82;US\n", + "Chevrolet Camaro;27.0;4;151.0;90.00;2950.;17.3;82;US\n", + "Ford Mustang GL;27.0;4;140.0;86.00;2790.;15.6;82;US\n", + "Volkswagen Pickup;44.0;4;97.00;52.00;2130.;24.6;82;Europe\n", + "Dodge Rampage;32.0;4;135.0;84.00;2295.;11.6;82;US\n", + "Ford Ranger;28.0;4;120.0;79.00;2625.;18.6;82;US\n", + "Chevy S-10;31.0;4;119.0;82.00;2720.;19.4;82;US\n" + ] + } + ], + "source": [ + "f = open('cars.csv')\n", + "for row in f:\n", + " print(row, end='')\n", + "f.close() " + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def parse_data_row(row):\n", + " row = row.strip('\\n').split(';')\n", + " return row[0], float(row[1])\n", + "\n", + "def max_mpg(data):\n", + " # get an iterator for data (which should be an iterable of some kind)\n", + " max_mpg = 0\n", + " for row in data:\n", + " _, mpg = parse_data_row(row)\n", + " if mpg > max_mpg:\n", + " max_mpg = mpg\n", + " return max_mpg" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "46.6\n" + ] + } + ], + "source": [ + "f = open('cars.csv')\n", + "next(f)\n", + "next(f)\n", + "print(max_mpg(f))\n", + "f.close()" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def list_data(data, mpg_max):\n", + " for row in data:\n", + " car, mpg = parse_data_row(row)\n", + " mpg_perc = mpg / mpg_max * 100\n", + " print(f'{car}: {mpg_perc:.2f}%')" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Chevrolet Chevelle Malibu: 38.63%\n", + "Buick Skylark 320: 32.19%\n", + "Plymouth Satellite: 38.63%\n", + "AMC Rebel SST: 34.33%\n", + "Ford Torino: 36.48%\n", + "Ford Galaxie 500: 32.19%\n", + "Chevrolet Impala: 30.04%\n", + "Plymouth Fury iii: 30.04%\n", + "Pontiac Catalina: 30.04%\n", + "AMC Ambassador DPL: 32.19%\n", + "Citroen DS-21 Pallas: 0.00%\n", + "Chevrolet Chevelle Concours (sw): 0.00%\n", + "Ford Torino (sw): 0.00%\n", + "Plymouth Satellite (sw): 0.00%\n", + "AMC Rebel SST (sw): 0.00%\n", + "Dodge Challenger SE: 32.19%\n", + "Plymouth 'Cuda 340: 30.04%\n", + "Ford Mustang Boss 302: 0.00%\n", + "Chevrolet Monte Carlo: 32.19%\n", + "Buick Estate Wagon (sw): 30.04%\n", + "Toyota Corolla Mark ii: 51.50%\n", + "Plymouth Duster: 47.21%\n", + "AMC Hornet: 38.63%\n", + "Ford Maverick: 45.06%\n", + "Datsun PL510: 57.94%\n", + "Volkswagen 1131 Deluxe Sedan: 55.79%\n", + "Peugeot 504: 53.65%\n", + "Audi 100 LS: 51.50%\n", + "Saab 99e: 53.65%\n", + "BMW 2002: 55.79%\n", + "AMC Gremlin: 45.06%\n", + "Ford F250: 21.46%\n", + "Chevy C20: 21.46%\n", + "Dodge D200: 23.61%\n", + "Hi 1200D: 19.31%\n", + "Datsun PL510: 57.94%\n", + "Chevrolet Vega 2300: 60.09%\n", + "Toyota Corolla: 53.65%\n", + "Ford Pinto: 53.65%\n", + "Volkswagen Super Beetle 117: 0.00%\n", + "AMC Gremlin: 40.77%\n", + "Plymouth Satellite Custom: 34.33%\n", + "Chevrolet Chevelle Malibu: 36.48%\n", + "Ford Torino 500: 40.77%\n", + "AMC Matador: 38.63%\n", + "Chevrolet Impala: 30.04%\n", + "Pontiac Catalina Brougham: 30.04%\n", + "Ford Galaxie 500: 30.04%\n", + "Plymouth Fury iii: 30.04%\n", + "Dodge Monaco (sw): 25.75%\n", + "Ford Country Squire (sw): 27.90%\n", + "Pontiac Safari (sw): 27.90%\n", + "AMC Hornet Sportabout (sw): 38.63%\n", + "Chevrolet Vega (sw): 47.21%\n", + "Pontiac Firebird: 40.77%\n", + "Ford Mustang: 38.63%\n", + "Mercury Capri 2000: 49.36%\n", + "Opel 1900: 60.09%\n", + "Peugeot 304: 64.38%\n", + "Fiat 124B: 64.38%\n", + "Toyota Corolla 1200: 66.52%\n", + "Datsun 1200: 75.11%\n", + "Volkswagen Model 111: 57.94%\n", + "Plymouth Cricket: 55.79%\n", + "Toyota Corolla Hardtop: 51.50%\n", + "Dodge Colt Hardtop: 53.65%\n", + "Volkswagen Type 3: 49.36%\n", + "Chevrolet Vega: 42.92%\n", + "Ford Pinto Runabout: 45.06%\n", + "Chevrolet Impala: 27.90%\n", + "Pontiac Catalina: 30.04%\n", + "Plymouth Fury III: 32.19%\n", + "Ford Galaxie 500: 30.04%\n", + "AMC Ambassador SST: 36.48%\n", + "Mercury Marquis: 23.61%\n", + "Buick LeSabre Custom: 27.90%\n", + "Oldsmobile Delta 88 Royale: 25.75%\n", + "Chrysler Newport Royal: 27.90%\n", + "Mazda RX2 Coupe: 40.77%\n", + "AMC Matador (sw): 32.19%\n", + "Chevrolet Chevelle Concours (sw): 27.90%\n", + "Ford Gran Torino (sw): 27.90%\n", + "Plymouth Satellite Custom (sw): 30.04%\n", + "Volvo 145e (sw): 38.63%\n", + "Volkswagen 411 (sw): 47.21%\n", + "Peugeot 504 (sw): 45.06%\n", + "Renault 12 (sw): 55.79%\n", + "Ford Pinto (sw): 47.21%\n", + "Datsun 510 (sw): 60.09%\n", + "Toyota Corolla Mark II (sw): 49.36%\n", + "Dodge Colt (sw): 60.09%\n", + "Toyota Corolla 1600 (sw): 57.94%\n", + "Buick Century 350: 27.90%\n", + "AMC Matador: 30.04%\n", + "Chevrolet Malibu: 27.90%\n", + "Ford Gran Torino: 30.04%\n", + "Dodge Coronet Custom: 32.19%\n", + "Mercury Marquis Brougham: 25.75%\n", + "Chevrolet Caprice Classic: 27.90%\n", + "Ford LTD: 27.90%\n", + "Plymouth Fury Gran Sedan: 30.04%\n", + "Chrysler New Yorker Brougham: 27.90%\n", + "Buick Electra 225 Custom: 25.75%\n", + "AMC Ambassador Brougham: 27.90%\n", + "Plymouth Valiant: 38.63%\n", + "Chevrolet Nova Custom: 34.33%\n", + "AMC Hornet: 38.63%\n", + "Ford Maverick: 38.63%\n", + "Plymouth Duster: 49.36%\n", + "Volkswagen Super Beetle: 55.79%\n", + "Chevrolet Impala: 23.61%\n", + "Ford Country: 25.75%\n", + "Plymouth Custom Suburb: 27.90%\n", + "Oldsmobile Vista Cruiser: 25.75%\n", + "AMC Gremlin: 38.63%\n", + "Toyota Camry: 42.92%\n", + "Chevrolet Vega: 45.06%\n", + "Datsun 610: 47.21%\n", + "Mazda RX3: 38.63%\n", + "Ford Pinto: 40.77%\n", + "Mercury Capri v6: 45.06%\n", + "Fiat 124 Sport Coupe: 55.79%\n", + "Chevrolet Monte Carlo S: 32.19%\n", + "Pontiac Grand Prix: 34.33%\n", + "Fiat 128: 62.23%\n", + "Opel Manta: 51.50%\n", + "Audi 100LS: 42.92%\n", + "Volvo 144ea: 40.77%\n", + "Dodge Dart Custom: 32.19%\n", + "Saab 99le: 51.50%\n", + "Toyota Mark II: 42.92%\n", + "Oldsmobile Omega: 23.61%\n", + "Plymouth Duster: 42.92%\n", + "Ford Maverick: 45.06%\n", + "AMC Hornet: 40.77%\n", + "Chevrolet Nova: 32.19%\n", + "Datsun B210: 66.52%\n", + "Ford Pinto: 55.79%\n", + "Toyota Corolla 1200: 68.67%\n", + "Chevrolet Vega: 53.65%\n", + "Chevrolet Chevelle Malibu Classic: 34.33%\n", + "AMC Matador: 34.33%\n", + "Plymouth Satellite Sebring: 38.63%\n", + "Ford Gran Torino: 34.33%\n", + "Buick Century Luxus (sw): 27.90%\n", + "Dodge Coronet Custom (sw): 30.04%\n", + "Ford Gran Torino (sw): 30.04%\n", + "AMC Matador (sw): 30.04%\n", + "Audi Fox: 62.23%\n", + "Volkswagen Dasher: 55.79%\n", + "Opel Manta: 55.79%\n", + "Toyota Corolla: 66.52%\n", + "Datsun 710: 68.67%\n", + "Dodge Colt: 60.09%\n", + "Fiat 128: 51.50%\n", + "Fiat 124 TC: 55.79%\n", + "Honda Civic: 51.50%\n", + "Subaru: 55.79%\n", + "Fiat x1.9: 66.52%\n", + "Plymouth Valiant Custom: 40.77%\n", + "Chevrolet Nova: 38.63%\n", + "Mercury Monarch: 32.19%\n", + "Ford Maverick: 32.19%\n", + "Pontiac Catalina: 34.33%\n", + "Chevrolet Bel Air: 32.19%\n", + "Plymouth Grand Fury: 34.33%\n", + "Ford LTD: 30.04%\n", + "Buick Century: 36.48%\n", + "Chevrolete Chevelle Malibu: 34.33%\n", + "AMC Matador: 32.19%\n", + "Plymouth Fury: 38.63%\n", + "Buick Skyhawk: 45.06%\n", + "Chevrolet Monza 2+2: 42.92%\n", + "Ford Mustang II: 27.90%\n", + "Toyota Corolla: 62.23%\n", + "Ford Pinto: 49.36%\n", + "AMC Gremlin: 42.92%\n", + "Pontiac Astro: 49.36%\n", + "Toyota Corolla: 51.50%\n", + "Volkswagen Dasher: 53.65%\n", + "Datsun 710: 51.50%\n", + "Ford Pinto: 38.63%\n", + "Volkswagen Rabbit: 62.23%\n", + "AMC Pacer: 40.77%\n", + "Audi 100LS: 49.36%\n", + "Peugeot 504: 49.36%\n", + "Volvo 244DL: 47.21%\n", + "Saab 99LE: 53.65%\n", + "Honda Civic CVCC: 70.82%\n", + "Fiat 131: 60.09%\n", + "Opel 1900: 53.65%\n", + "Capri ii: 53.65%\n", + "Dodge Colt: 55.79%\n", + "Renault 12tl: 57.94%\n", + "Chevrolet Chevelle Malibu Classic: 37.55%\n", + "Dodge Coronet Brougham: 34.33%\n", + "AMC Matador: 33.26%\n", + "Ford Gran Torino: 31.12%\n", + "Plymouth Valiant: 47.21%\n", + "Chevrolet Nova: 47.21%\n", + "Ford Maverick: 51.50%\n", + "AMC Hornet: 48.28%\n", + "Chevrolet Chevette: 62.23%\n", + "Chevrolet Woody: 52.58%\n", + "Volkswagen Rabbit: 62.23%\n", + "Honda Civic: 70.82%\n", + "Dodge Aspen SE: 42.92%\n", + "Ford Grenada ghia: 38.63%\n", + "Pontiac Ventura SJ: 39.70%\n", + "AMC Pacer d/l: 37.55%\n", + "Volkswagen Rabbit: 63.30%\n", + "Datsun B-210: 68.67%\n", + "Toyota Corolla: 60.09%\n", + "Ford Pinto: 56.87%\n", + "Volvo 245: 42.92%\n", + "Plymouth Volare Premier v8: 27.90%\n", + "Peugeot 504: 40.77%\n", + "Toyota Mark II: 40.77%\n", + "Mercedes-Benz 280s: 35.41%\n", + "Cadillac Seville: 35.41%\n", + "Chevrolet C10: 27.90%\n", + "Ford F108: 27.90%\n", + "Dodge D100: 27.90%\n", + "Honda Accord CVCC: 67.60%\n", + "Buick Opel Isuzu Deluxe: 64.38%\n", + "Renault 5 GTL: 77.25%\n", + "Plymouth Arrow GS: 54.72%\n", + "Datsun F-10 Hatchback: 71.89%\n", + "Chevrolet Caprice Classic: 37.55%\n", + "Oldsmobile Cutlass Supreme: 36.48%\n", + "Dodge Monaco Brougham: 33.26%\n", + "Mercury Cougar Brougham: 32.19%\n", + "Chevrolet Concours: 37.55%\n", + "Buick Skylark: 43.99%\n", + "Plymouth Volare Custom: 40.77%\n", + "Ford Grenada: 39.70%\n", + "Pontiac Grand Prix LJ: 34.33%\n", + "Chevrolet Monte Carlo Landau: 33.26%\n", + "Chrysler Cordoba: 33.26%\n", + "Ford Thunderbird: 34.33%\n", + "Volkswagen Rabbit Custom: 62.23%\n", + "Pontiac Sunbird Coupe: 52.58%\n", + "Toyota Corolla Liftback: 55.79%\n", + "Ford Mustang II 2+2: 54.72%\n", + "Chevrolet Chevette: 65.45%\n", + "Dodge Colt m/m: 71.89%\n", + "Subaru DL: 64.38%\n", + "Volkswagen Dasher: 65.45%\n", + "Datsun 810: 47.21%\n", + "BMW 320i: 46.14%\n", + "Mazda RX-4: 46.14%\n", + "Volkswagen Rabbit Custom Diesel: 92.49%\n", + "Ford Fiesta: 77.47%\n", + "Mazda GLC Deluxe: 70.39%\n", + "Datsun B210 GX: 84.55%\n", + "Honda Civic CVCC: 77.47%\n", + "Oldsmobile Cutlass Salon Brougham: 42.70%\n", + "Dodge Diplomat: 41.63%\n", + "Mercury Monarch ghia: 43.35%\n", + "Pontiac Phoenix LJ: 41.20%\n", + "Chevrolet Malibu: 43.99%\n", + "Ford Fairmont (auto): 43.35%\n", + "Ford Fairmont (man): 53.86%\n", + "Plymouth Volare: 43.99%\n", + "AMC Concord: 41.63%\n", + "Buick Century Special: 44.21%\n", + "Mercury Zephyr: 44.64%\n", + "Dodge Aspen: 39.91%\n", + "AMC Concord d/l: 38.84%\n", + "Chevrolet Monte Carlo Landau: 41.20%\n", + "Buick Regal Sport Coupe (turbo): 37.98%\n", + "Ford Futura: 38.84%\n", + "Dodge Magnum XE: 37.55%\n", + "Chevrolet Chevette: 64.38%\n", + "Toyota Corolla: 59.01%\n", + "Datsun 510: 58.37%\n", + "Dodge Omni: 66.31%\n", + "Toyota Celica GT Liftback: 45.28%\n", + "Plymouth Sapporo: 49.79%\n", + "Oldsmobile Starfire SX: 51.07%\n", + "Datsun 200-SX: 51.29%\n", + "Audi 5000: 43.56%\n", + "Volvo 264gl: 36.48%\n", + "Saab 99gle: 46.35%\n", + "Peugeot 604sl: 34.76%\n", + "Volkswagen Scirocco: 67.60%\n", + "Honda Accord LX: 63.30%\n", + "Pontiac Lemans V6: 46.14%\n", + "Mercury Zephyr 6: 42.49%\n", + "Ford Fairmont 4: 47.85%\n", + "AMC Concord DL 6: 43.35%\n", + "Dodge Aspen 6: 44.21%\n", + "Chevrolet Caprice Classic: 36.48%\n", + "Ford LTD Landau: 37.77%\n", + "Mercury Grand Marquis: 35.41%\n", + "Dodge St. Regis: 39.06%\n", + "Buick Estate Wagon (sw): 36.27%\n", + "Ford Country Squire (sw): 33.26%\n", + "Chevrolet Malibu Classic (sw): 41.20%\n", + "Chrysler Lebaron Town @ Country (sw): 39.70%\n", + "Volkswagen Rabbit Custom: 68.45%\n", + "Mazda GLC Deluxe: 73.18%\n", + "Dodge Colt Hatchback Custom: 76.61%\n", + "AMC Spirit DL: 58.80%\n", + "Mercedes Benz 300d: 54.51%\n", + "Cadillac Eldorado: 49.36%\n", + "Peugeot 504: 58.37%\n", + "Oldsmobile Cutlass Salon Brougham: 51.29%\n", + "Plymouth Horizon: 73.39%\n", + "Plymouth Horizon TC3: 74.03%\n", + "Datsun 210: 68.24%\n", + "Fiat Strada Custom: 80.04%\n", + "Buick Skylark Limited: 60.94%\n", + "Chevrolet Citation: 61.80%\n", + "Oldsmobile Omega Brougham: 57.51%\n", + "Pontiac Phoenix: 71.89%\n", + "Volkswagen Rabbit: 89.06%\n", + "Toyota Corolla Tercel: 81.76%\n", + "Chevrolet Chevette: 68.88%\n", + "Datsun 310: 79.83%\n", + "Chevrolet Citation: 60.09%\n", + "Ford Fairmont: 56.65%\n", + "AMC Concord: 52.15%\n", + "Dodge Aspen: 40.99%\n", + "Audi 4000: 73.61%\n", + "Toyota Corolla Liftback: 63.95%\n", + "Mazda 626: 67.17%\n", + "Datsun 510 Hatchback: 79.40%\n", + "Toyota Corolla: 69.10%\n", + "Mazda GLC: 100.00%\n", + "Dodge Colt: 59.87%\n", + "Datsun 210: 87.55%\n", + "Volkswagen Rabbit C (Diesel): 95.06%\n", + "Volkswagen Dasher (diesel): 93.13%\n", + "Audi 5000s (diesel): 78.11%\n", + "Mercedes-Benz 240d: 64.38%\n", + "Honda Civic 1500 gl: 95.71%\n", + "Renault Lecar Deluxe: 87.77%\n", + "Subaru DL: 72.53%\n", + "Volkswagen Rabbit: 63.95%\n", + "Datsun 280-ZX: 70.17%\n", + "Mazda RX-7 GS: 50.86%\n", + "Triumph TR7 Coupe: 75.11%\n", + "Ford Mustang Cobra: 50.64%\n", + "Honda Accord: 69.53%\n", + "Plymouth Reliant: 58.37%\n", + "Buick Skylark: 57.08%\n", + "Dodge Aries Wagon (sw): 55.36%\n", + "Chevrolet Citation: 50.43%\n", + "Plymouth Reliant: 64.38%\n", + "Toyota Starlet: 83.91%\n", + "Plymouth Champ: 83.69%\n", + "Honda Civic 1300: 75.32%\n", + "Subaru: 69.31%\n", + "Datsun 210 MPG: 79.40%\n", + "Toyota Tercel: 80.90%\n", + "Mazda GLC 4: 73.18%\n", + "Plymouth Horizon 4: 74.46%\n", + "Ford Escort 4W: 73.82%\n", + "Ford Escort 2H: 64.16%\n", + "Volkswagen Jetta: 70.82%\n", + "Renault 18i: 74.03%\n", + "Honda Prelude: 72.32%\n", + "Toyota Corolla: 69.53%\n", + "Datsun 200SX: 70.60%\n", + "Mazda 626: 67.81%\n", + "Peugeot 505s Turbo Diesel: 60.30%\n", + "Saab 900s: 0.00%\n", + "Volvo Diesel: 65.88%\n", + "Toyota Cressida: 54.51%\n", + "Datsun 810 Maxima: 51.93%\n", + "Buick Century: 48.07%\n", + "Oldsmobile Cutlass LS: 57.08%\n", + "Ford Grenada gl: 43.35%\n", + "Chrysler Lebaron Salon: 37.77%\n", + "Chevrolet Cavalier: 60.09%\n", + "Chevrolet Cavalier Wagon: 57.94%\n", + "Chevrolet Cavalier 2-door: 72.96%\n", + "Pontiac J2000 SE Hatchback: 66.52%\n", + "Dodge Aries SE: 62.23%\n", + "Pontiac Phoenix: 57.94%\n", + "Ford Fairmont Futura: 51.50%\n", + "AMC Concord DL: 49.36%\n", + "Volkswagen Rabbit l: 77.25%\n", + "Mazda GLC Custom l: 79.40%\n", + "Mazda GLC Custom: 66.52%\n", + "Plymouth Horizon Miser: 81.55%\n", + "Mercury Lynx l: 77.25%\n", + "Nissan Stanza XE: 77.25%\n", + "Honda Accord: 77.25%\n", + "Toyota Corolla: 72.96%\n", + "Honda Civic: 81.55%\n", + "Honda Civic (auto): 68.67%\n", + "Datsun 310 GX: 81.55%\n", + "Buick Century Limited: 53.65%\n", + "Oldsmobile Cutlass Ciera (diesel): 81.55%\n", + "Chrysler Lebaron Medallion: 55.79%\n", + "Ford Grenada l: 47.21%\n", + "Toyota Celica GT: 68.67%\n", + "Dodge Charger 2.2: 77.25%\n", + "Chevrolet Camaro: 57.94%\n", + "Ford Mustang GL: 57.94%\n", + "Volkswagen Pickup: 94.42%\n", + "Dodge Rampage: 68.67%\n", + "Ford Ranger: 60.09%\n", + "Chevy S-10: 66.52%\n" + ] + } + ], + "source": [ + "f = open('cars.csv')\n", + "next(f), next(f)\n", + "list_data(f, 46.6)\n", + "f.close()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now let's try and put these together:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "max=46.6\n" + ] + } + ], + "source": [ + "with open('cars.csv') as f:\n", + " next(f)\n", + " next(f)\n", + " max_ = max_mpg(f)\n", + " print(f'max={max_}')\n", + " list_data(f, max_)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "No output from `list_data`!!\n", + "\n", + "That's because when we called `list_data` we had already exhausted the data file in the call to `max_mpg`." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Our only option is to either create the iterator twice:" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Chevrolet Chevelle Malibu: 38.63%\n", + "Buick Skylark 320: 32.19%\n", + "Plymouth Satellite: 38.63%\n", + "AMC Rebel SST: 34.33%\n", + "Ford Torino: 36.48%\n", + "Ford Galaxie 500: 32.19%\n", + "Chevrolet Impala: 30.04%\n", + "Plymouth Fury iii: 30.04%\n", + "Pontiac Catalina: 30.04%\n", + "AMC Ambassador DPL: 32.19%\n", + "Citroen DS-21 Pallas: 0.00%\n", + "Chevrolet Chevelle Concours (sw): 0.00%\n", + "Ford Torino (sw): 0.00%\n", + "Plymouth Satellite (sw): 0.00%\n", + "AMC Rebel SST (sw): 0.00%\n", + "Dodge Challenger SE: 32.19%\n", + "Plymouth 'Cuda 340: 30.04%\n", + "Ford Mustang Boss 302: 0.00%\n", + "Chevrolet Monte Carlo: 32.19%\n", + "Buick Estate Wagon (sw): 30.04%\n", + "Toyota Corolla Mark ii: 51.50%\n", + "Plymouth Duster: 47.21%\n", + "AMC Hornet: 38.63%\n", + "Ford Maverick: 45.06%\n", + "Datsun PL510: 57.94%\n", + "Volkswagen 1131 Deluxe Sedan: 55.79%\n", + "Peugeot 504: 53.65%\n", + "Audi 100 LS: 51.50%\n", + "Saab 99e: 53.65%\n", + "BMW 2002: 55.79%\n", + "AMC Gremlin: 45.06%\n", + "Ford F250: 21.46%\n", + "Chevy C20: 21.46%\n", + "Dodge D200: 23.61%\n", + "Hi 1200D: 19.31%\n", + "Datsun PL510: 57.94%\n", + "Chevrolet Vega 2300: 60.09%\n", + "Toyota Corolla: 53.65%\n", + "Ford Pinto: 53.65%\n", + "Volkswagen Super Beetle 117: 0.00%\n", + "AMC Gremlin: 40.77%\n", + "Plymouth Satellite Custom: 34.33%\n", + "Chevrolet Chevelle Malibu: 36.48%\n", + "Ford Torino 500: 40.77%\n", + "AMC Matador: 38.63%\n", + "Chevrolet Impala: 30.04%\n", + "Pontiac Catalina Brougham: 30.04%\n", + "Ford Galaxie 500: 30.04%\n", + "Plymouth Fury iii: 30.04%\n", + "Dodge Monaco (sw): 25.75%\n", + "Ford Country Squire (sw): 27.90%\n", + "Pontiac Safari (sw): 27.90%\n", + "AMC Hornet Sportabout (sw): 38.63%\n", + "Chevrolet Vega (sw): 47.21%\n", + "Pontiac Firebird: 40.77%\n", + "Ford Mustang: 38.63%\n", + "Mercury Capri 2000: 49.36%\n", + "Opel 1900: 60.09%\n", + "Peugeot 304: 64.38%\n", + "Fiat 124B: 64.38%\n", + "Toyota Corolla 1200: 66.52%\n", + "Datsun 1200: 75.11%\n", + "Volkswagen Model 111: 57.94%\n", + "Plymouth Cricket: 55.79%\n", + "Toyota Corolla Hardtop: 51.50%\n", + "Dodge Colt Hardtop: 53.65%\n", + "Volkswagen Type 3: 49.36%\n", + "Chevrolet Vega: 42.92%\n", + "Ford Pinto Runabout: 45.06%\n", + "Chevrolet Impala: 27.90%\n", + "Pontiac Catalina: 30.04%\n", + "Plymouth Fury III: 32.19%\n", + "Ford Galaxie 500: 30.04%\n", + "AMC Ambassador SST: 36.48%\n", + "Mercury Marquis: 23.61%\n", + "Buick LeSabre Custom: 27.90%\n", + "Oldsmobile Delta 88 Royale: 25.75%\n", + "Chrysler Newport Royal: 27.90%\n", + "Mazda RX2 Coupe: 40.77%\n", + "AMC Matador (sw): 32.19%\n", + "Chevrolet Chevelle Concours (sw): 27.90%\n", + "Ford Gran Torino (sw): 27.90%\n", + "Plymouth Satellite Custom (sw): 30.04%\n", + "Volvo 145e (sw): 38.63%\n", + "Volkswagen 411 (sw): 47.21%\n", + "Peugeot 504 (sw): 45.06%\n", + "Renault 12 (sw): 55.79%\n", + "Ford Pinto (sw): 47.21%\n", + "Datsun 510 (sw): 60.09%\n", + "Toyota Corolla Mark II (sw): 49.36%\n", + "Dodge Colt (sw): 60.09%\n", + "Toyota Corolla 1600 (sw): 57.94%\n", + "Buick Century 350: 27.90%\n", + "AMC Matador: 30.04%\n", + "Chevrolet Malibu: 27.90%\n", + "Ford Gran Torino: 30.04%\n", + "Dodge Coronet Custom: 32.19%\n", + "Mercury Marquis Brougham: 25.75%\n", + "Chevrolet Caprice Classic: 27.90%\n", + "Ford LTD: 27.90%\n", + "Plymouth Fury Gran Sedan: 30.04%\n", + "Chrysler New Yorker Brougham: 27.90%\n", + "Buick Electra 225 Custom: 25.75%\n", + "AMC Ambassador Brougham: 27.90%\n", + "Plymouth Valiant: 38.63%\n", + "Chevrolet Nova Custom: 34.33%\n", + "AMC Hornet: 38.63%\n", + "Ford Maverick: 38.63%\n", + "Plymouth Duster: 49.36%\n", + "Volkswagen Super Beetle: 55.79%\n", + "Chevrolet Impala: 23.61%\n", + "Ford Country: 25.75%\n", + "Plymouth Custom Suburb: 27.90%\n", + "Oldsmobile Vista Cruiser: 25.75%\n", + "AMC Gremlin: 38.63%\n", + "Toyota Camry: 42.92%\n", + "Chevrolet Vega: 45.06%\n", + "Datsun 610: 47.21%\n", + "Mazda RX3: 38.63%\n", + "Ford Pinto: 40.77%\n", + "Mercury Capri v6: 45.06%\n", + "Fiat 124 Sport Coupe: 55.79%\n", + "Chevrolet Monte Carlo S: 32.19%\n", + "Pontiac Grand Prix: 34.33%\n", + "Fiat 128: 62.23%\n", + "Opel Manta: 51.50%\n", + "Audi 100LS: 42.92%\n", + "Volvo 144ea: 40.77%\n", + "Dodge Dart Custom: 32.19%\n", + "Saab 99le: 51.50%\n", + "Toyota Mark II: 42.92%\n", + "Oldsmobile Omega: 23.61%\n", + "Plymouth Duster: 42.92%\n", + "Ford Maverick: 45.06%\n", + "AMC Hornet: 40.77%\n", + "Chevrolet Nova: 32.19%\n", + "Datsun B210: 66.52%\n", + "Ford Pinto: 55.79%\n", + "Toyota Corolla 1200: 68.67%\n", + "Chevrolet Vega: 53.65%\n", + "Chevrolet Chevelle Malibu Classic: 34.33%\n", + "AMC Matador: 34.33%\n", + "Plymouth Satellite Sebring: 38.63%\n", + "Ford Gran Torino: 34.33%\n", + "Buick Century Luxus (sw): 27.90%\n", + "Dodge Coronet Custom (sw): 30.04%\n", + "Ford Gran Torino (sw): 30.04%\n", + "AMC Matador (sw): 30.04%\n", + "Audi Fox: 62.23%\n", + "Volkswagen Dasher: 55.79%\n", + "Opel Manta: 55.79%\n", + "Toyota Corolla: 66.52%\n", + "Datsun 710: 68.67%\n", + "Dodge Colt: 60.09%\n", + "Fiat 128: 51.50%\n", + "Fiat 124 TC: 55.79%\n", + "Honda Civic: 51.50%\n", + "Subaru: 55.79%\n", + "Fiat x1.9: 66.52%\n", + "Plymouth Valiant Custom: 40.77%\n", + "Chevrolet Nova: 38.63%\n", + "Mercury Monarch: 32.19%\n", + "Ford Maverick: 32.19%\n", + "Pontiac Catalina: 34.33%\n", + "Chevrolet Bel Air: 32.19%\n", + "Plymouth Grand Fury: 34.33%\n", + "Ford LTD: 30.04%\n", + "Buick Century: 36.48%\n", + "Chevrolete Chevelle Malibu: 34.33%\n", + "AMC Matador: 32.19%\n", + "Plymouth Fury: 38.63%\n", + "Buick Skyhawk: 45.06%\n", + "Chevrolet Monza 2+2: 42.92%\n", + "Ford Mustang II: 27.90%\n", + "Toyota Corolla: 62.23%\n", + "Ford Pinto: 49.36%\n", + "AMC Gremlin: 42.92%\n", + "Pontiac Astro: 49.36%\n", + "Toyota Corolla: 51.50%\n", + "Volkswagen Dasher: 53.65%\n", + "Datsun 710: 51.50%\n", + "Ford Pinto: 38.63%\n", + "Volkswagen Rabbit: 62.23%\n", + "AMC Pacer: 40.77%\n", + "Audi 100LS: 49.36%\n", + "Peugeot 504: 49.36%\n", + "Volvo 244DL: 47.21%\n", + "Saab 99LE: 53.65%\n", + "Honda Civic CVCC: 70.82%\n", + "Fiat 131: 60.09%\n", + "Opel 1900: 53.65%\n", + "Capri ii: 53.65%\n", + "Dodge Colt: 55.79%\n", + "Renault 12tl: 57.94%\n", + "Chevrolet Chevelle Malibu Classic: 37.55%\n", + "Dodge Coronet Brougham: 34.33%\n", + "AMC Matador: 33.26%\n", + "Ford Gran Torino: 31.12%\n", + "Plymouth Valiant: 47.21%\n", + "Chevrolet Nova: 47.21%\n", + "Ford Maverick: 51.50%\n", + "AMC Hornet: 48.28%\n", + "Chevrolet Chevette: 62.23%\n", + "Chevrolet Woody: 52.58%\n", + "Volkswagen Rabbit: 62.23%\n", + "Honda Civic: 70.82%\n", + "Dodge Aspen SE: 42.92%\n", + "Ford Grenada ghia: 38.63%\n", + "Pontiac Ventura SJ: 39.70%\n", + "AMC Pacer d/l: 37.55%\n", + "Volkswagen Rabbit: 63.30%\n", + "Datsun B-210: 68.67%\n", + "Toyota Corolla: 60.09%\n", + "Ford Pinto: 56.87%\n", + "Volvo 245: 42.92%\n", + "Plymouth Volare Premier v8: 27.90%\n", + "Peugeot 504: 40.77%\n", + "Toyota Mark II: 40.77%\n", + "Mercedes-Benz 280s: 35.41%\n", + "Cadillac Seville: 35.41%\n", + "Chevrolet C10: 27.90%\n", + "Ford F108: 27.90%\n", + "Dodge D100: 27.90%\n", + "Honda Accord CVCC: 67.60%\n", + "Buick Opel Isuzu Deluxe: 64.38%\n", + "Renault 5 GTL: 77.25%\n", + "Plymouth Arrow GS: 54.72%\n", + "Datsun F-10 Hatchback: 71.89%\n", + "Chevrolet Caprice Classic: 37.55%\n", + "Oldsmobile Cutlass Supreme: 36.48%\n", + "Dodge Monaco Brougham: 33.26%\n", + "Mercury Cougar Brougham: 32.19%\n", + "Chevrolet Concours: 37.55%\n", + "Buick Skylark: 43.99%\n", + "Plymouth Volare Custom: 40.77%\n", + "Ford Grenada: 39.70%\n", + "Pontiac Grand Prix LJ: 34.33%\n", + "Chevrolet Monte Carlo Landau: 33.26%\n", + "Chrysler Cordoba: 33.26%\n", + "Ford Thunderbird: 34.33%\n", + "Volkswagen Rabbit Custom: 62.23%\n", + "Pontiac Sunbird Coupe: 52.58%\n", + "Toyota Corolla Liftback: 55.79%\n", + "Ford Mustang II 2+2: 54.72%\n", + "Chevrolet Chevette: 65.45%\n", + "Dodge Colt m/m: 71.89%\n", + "Subaru DL: 64.38%\n", + "Volkswagen Dasher: 65.45%\n", + "Datsun 810: 47.21%\n", + "BMW 320i: 46.14%\n", + "Mazda RX-4: 46.14%\n", + "Volkswagen Rabbit Custom Diesel: 92.49%\n", + "Ford Fiesta: 77.47%\n", + "Mazda GLC Deluxe: 70.39%\n", + "Datsun B210 GX: 84.55%\n", + "Honda Civic CVCC: 77.47%\n", + "Oldsmobile Cutlass Salon Brougham: 42.70%\n", + "Dodge Diplomat: 41.63%\n", + "Mercury Monarch ghia: 43.35%\n", + "Pontiac Phoenix LJ: 41.20%\n", + "Chevrolet Malibu: 43.99%\n", + "Ford Fairmont (auto): 43.35%\n", + "Ford Fairmont (man): 53.86%\n", + "Plymouth Volare: 43.99%\n", + "AMC Concord: 41.63%\n", + "Buick Century Special: 44.21%\n", + "Mercury Zephyr: 44.64%\n", + "Dodge Aspen: 39.91%\n", + "AMC Concord d/l: 38.84%\n", + "Chevrolet Monte Carlo Landau: 41.20%\n", + "Buick Regal Sport Coupe (turbo): 37.98%\n", + "Ford Futura: 38.84%\n", + "Dodge Magnum XE: 37.55%\n", + "Chevrolet Chevette: 64.38%\n", + "Toyota Corolla: 59.01%\n", + "Datsun 510: 58.37%\n", + "Dodge Omni: 66.31%\n", + "Toyota Celica GT Liftback: 45.28%\n", + "Plymouth Sapporo: 49.79%\n", + "Oldsmobile Starfire SX: 51.07%\n", + "Datsun 200-SX: 51.29%\n", + "Audi 5000: 43.56%\n", + "Volvo 264gl: 36.48%\n", + "Saab 99gle: 46.35%\n", + "Peugeot 604sl: 34.76%\n", + "Volkswagen Scirocco: 67.60%\n", + "Honda Accord LX: 63.30%\n", + "Pontiac Lemans V6: 46.14%\n", + "Mercury Zephyr 6: 42.49%\n", + "Ford Fairmont 4: 47.85%\n", + "AMC Concord DL 6: 43.35%\n", + "Dodge Aspen 6: 44.21%\n", + "Chevrolet Caprice Classic: 36.48%\n", + "Ford LTD Landau: 37.77%\n", + "Mercury Grand Marquis: 35.41%\n", + "Dodge St. Regis: 39.06%\n", + "Buick Estate Wagon (sw): 36.27%\n", + "Ford Country Squire (sw): 33.26%\n", + "Chevrolet Malibu Classic (sw): 41.20%\n", + "Chrysler Lebaron Town @ Country (sw): 39.70%\n", + "Volkswagen Rabbit Custom: 68.45%\n", + "Mazda GLC Deluxe: 73.18%\n", + "Dodge Colt Hatchback Custom: 76.61%\n", + "AMC Spirit DL: 58.80%\n", + "Mercedes Benz 300d: 54.51%\n", + "Cadillac Eldorado: 49.36%\n", + "Peugeot 504: 58.37%\n", + "Oldsmobile Cutlass Salon Brougham: 51.29%\n", + "Plymouth Horizon: 73.39%\n", + "Plymouth Horizon TC3: 74.03%\n", + "Datsun 210: 68.24%\n", + "Fiat Strada Custom: 80.04%\n", + "Buick Skylark Limited: 60.94%\n", + "Chevrolet Citation: 61.80%\n", + "Oldsmobile Omega Brougham: 57.51%\n", + "Pontiac Phoenix: 71.89%\n", + "Volkswagen Rabbit: 89.06%\n", + "Toyota Corolla Tercel: 81.76%\n", + "Chevrolet Chevette: 68.88%\n", + "Datsun 310: 79.83%\n", + "Chevrolet Citation: 60.09%\n", + "Ford Fairmont: 56.65%\n", + "AMC Concord: 52.15%\n", + "Dodge Aspen: 40.99%\n", + "Audi 4000: 73.61%\n", + "Toyota Corolla Liftback: 63.95%\n", + "Mazda 626: 67.17%\n", + "Datsun 510 Hatchback: 79.40%\n", + "Toyota Corolla: 69.10%\n", + "Mazda GLC: 100.00%\n", + "Dodge Colt: 59.87%\n", + "Datsun 210: 87.55%\n", + "Volkswagen Rabbit C (Diesel): 95.06%\n", + "Volkswagen Dasher (diesel): 93.13%\n", + "Audi 5000s (diesel): 78.11%\n", + "Mercedes-Benz 240d: 64.38%\n", + "Honda Civic 1500 gl: 95.71%\n", + "Renault Lecar Deluxe: 87.77%\n", + "Subaru DL: 72.53%\n", + "Volkswagen Rabbit: 63.95%\n", + "Datsun 280-ZX: 70.17%\n", + "Mazda RX-7 GS: 50.86%\n", + "Triumph TR7 Coupe: 75.11%\n", + "Ford Mustang Cobra: 50.64%\n", + "Honda Accord: 69.53%\n", + "Plymouth Reliant: 58.37%\n", + "Buick Skylark: 57.08%\n", + "Dodge Aries Wagon (sw): 55.36%\n", + "Chevrolet Citation: 50.43%\n", + "Plymouth Reliant: 64.38%\n", + "Toyota Starlet: 83.91%\n", + "Plymouth Champ: 83.69%\n", + "Honda Civic 1300: 75.32%\n", + "Subaru: 69.31%\n", + "Datsun 210 MPG: 79.40%\n", + "Toyota Tercel: 80.90%\n", + "Mazda GLC 4: 73.18%\n", + "Plymouth Horizon 4: 74.46%\n", + "Ford Escort 4W: 73.82%\n", + "Ford Escort 2H: 64.16%\n", + "Volkswagen Jetta: 70.82%\n", + "Renault 18i: 74.03%\n", + "Honda Prelude: 72.32%\n", + "Toyota Corolla: 69.53%\n", + "Datsun 200SX: 70.60%\n", + "Mazda 626: 67.81%\n", + "Peugeot 505s Turbo Diesel: 60.30%\n", + "Saab 900s: 0.00%\n", + "Volvo Diesel: 65.88%\n", + "Toyota Cressida: 54.51%\n", + "Datsun 810 Maxima: 51.93%\n", + "Buick Century: 48.07%\n", + "Oldsmobile Cutlass LS: 57.08%\n", + "Ford Grenada gl: 43.35%\n", + "Chrysler Lebaron Salon: 37.77%\n", + "Chevrolet Cavalier: 60.09%\n", + "Chevrolet Cavalier Wagon: 57.94%\n", + "Chevrolet Cavalier 2-door: 72.96%\n", + "Pontiac J2000 SE Hatchback: 66.52%\n", + "Dodge Aries SE: 62.23%\n", + "Pontiac Phoenix: 57.94%\n", + "Ford Fairmont Futura: 51.50%\n", + "AMC Concord DL: 49.36%\n", + "Volkswagen Rabbit l: 77.25%\n", + "Mazda GLC Custom l: 79.40%\n", + "Mazda GLC Custom: 66.52%\n", + "Plymouth Horizon Miser: 81.55%\n", + "Mercury Lynx l: 77.25%\n", + "Nissan Stanza XE: 77.25%\n", + "Honda Accord: 77.25%\n", + "Toyota Corolla: 72.96%\n", + "Honda Civic: 81.55%\n", + "Honda Civic (auto): 68.67%\n", + "Datsun 310 GX: 81.55%\n", + "Buick Century Limited: 53.65%\n", + "Oldsmobile Cutlass Ciera (diesel): 81.55%\n", + "Chrysler Lebaron Medallion: 55.79%\n", + "Ford Grenada l: 47.21%\n", + "Toyota Celica GT: 68.67%\n", + "Dodge Charger 2.2: 77.25%\n", + "Chevrolet Camaro: 57.94%\n", + "Ford Mustang GL: 57.94%\n", + "Volkswagen Pickup: 94.42%\n", + "Dodge Rampage: 68.67%\n", + "Ford Ranger: 60.09%\n", + "Chevy S-10: 66.52%\n" + ] + } + ], + "source": [ + "with open('cars.csv') as f:\n", + " next(f), next(f)\n", + " max_ = max_mpg(f)\n", + " \n", + "with open('cars.csv') as f:\n", + " next(f), next(f)\n", + " list_data(f, max_)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "or we could read the entire data set into a list first - but of course if the file is huge we will have some potential for running out memory:" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "with open('cars.csv') as f:\n", + " data = [row for row in f][2:]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "or, more simply:" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "with open('cars.csv') as f:\n", + " data = f.readlines()[2:]" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Chevrolet Chevelle Malibu: 38.63%\n", + "Buick Skylark 320: 32.19%\n", + "Plymouth Satellite: 38.63%\n", + "AMC Rebel SST: 34.33%\n", + "Ford Torino: 36.48%\n", + "Ford Galaxie 500: 32.19%\n", + "Chevrolet Impala: 30.04%\n", + "Plymouth Fury iii: 30.04%\n", + "Pontiac Catalina: 30.04%\n", + "AMC Ambassador DPL: 32.19%\n", + "Citroen DS-21 Pallas: 0.00%\n", + "Chevrolet Chevelle Concours (sw): 0.00%\n", + "Ford Torino (sw): 0.00%\n", + "Plymouth Satellite (sw): 0.00%\n", + "AMC Rebel SST (sw): 0.00%\n", + "Dodge Challenger SE: 32.19%\n", + "Plymouth 'Cuda 340: 30.04%\n", + "Ford Mustang Boss 302: 0.00%\n", + "Chevrolet Monte Carlo: 32.19%\n", + "Buick Estate Wagon (sw): 30.04%\n", + "Toyota Corolla Mark ii: 51.50%\n", + "Plymouth Duster: 47.21%\n", + "AMC Hornet: 38.63%\n", + "Ford Maverick: 45.06%\n", + "Datsun PL510: 57.94%\n", + "Volkswagen 1131 Deluxe Sedan: 55.79%\n", + "Peugeot 504: 53.65%\n", + "Audi 100 LS: 51.50%\n", + "Saab 99e: 53.65%\n", + "BMW 2002: 55.79%\n", + "AMC Gremlin: 45.06%\n", + "Ford F250: 21.46%\n", + "Chevy C20: 21.46%\n", + "Dodge D200: 23.61%\n", + "Hi 1200D: 19.31%\n", + "Datsun PL510: 57.94%\n", + "Chevrolet Vega 2300: 60.09%\n", + "Toyota Corolla: 53.65%\n", + "Ford Pinto: 53.65%\n", + "Volkswagen Super Beetle 117: 0.00%\n", + "AMC Gremlin: 40.77%\n", + "Plymouth Satellite Custom: 34.33%\n", + "Chevrolet Chevelle Malibu: 36.48%\n", + "Ford Torino 500: 40.77%\n", + "AMC Matador: 38.63%\n", + "Chevrolet Impala: 30.04%\n", + "Pontiac Catalina Brougham: 30.04%\n", + "Ford Galaxie 500: 30.04%\n", + "Plymouth Fury iii: 30.04%\n", + "Dodge Monaco (sw): 25.75%\n", + "Ford Country Squire (sw): 27.90%\n", + "Pontiac Safari (sw): 27.90%\n", + "AMC Hornet Sportabout (sw): 38.63%\n", + "Chevrolet Vega (sw): 47.21%\n", + "Pontiac Firebird: 40.77%\n", + "Ford Mustang: 38.63%\n", + "Mercury Capri 2000: 49.36%\n", + "Opel 1900: 60.09%\n", + "Peugeot 304: 64.38%\n", + "Fiat 124B: 64.38%\n", + "Toyota Corolla 1200: 66.52%\n", + "Datsun 1200: 75.11%\n", + "Volkswagen Model 111: 57.94%\n", + "Plymouth Cricket: 55.79%\n", + "Toyota Corolla Hardtop: 51.50%\n", + "Dodge Colt Hardtop: 53.65%\n", + "Volkswagen Type 3: 49.36%\n", + "Chevrolet Vega: 42.92%\n", + "Ford Pinto Runabout: 45.06%\n", + "Chevrolet Impala: 27.90%\n", + "Pontiac Catalina: 30.04%\n", + "Plymouth Fury III: 32.19%\n", + "Ford Galaxie 500: 30.04%\n", + "AMC Ambassador SST: 36.48%\n", + "Mercury Marquis: 23.61%\n", + "Buick LeSabre Custom: 27.90%\n", + "Oldsmobile Delta 88 Royale: 25.75%\n", + "Chrysler Newport Royal: 27.90%\n", + "Mazda RX2 Coupe: 40.77%\n", + "AMC Matador (sw): 32.19%\n", + "Chevrolet Chevelle Concours (sw): 27.90%\n", + "Ford Gran Torino (sw): 27.90%\n", + "Plymouth Satellite Custom (sw): 30.04%\n", + "Volvo 145e (sw): 38.63%\n", + "Volkswagen 411 (sw): 47.21%\n", + "Peugeot 504 (sw): 45.06%\n", + "Renault 12 (sw): 55.79%\n", + "Ford Pinto (sw): 47.21%\n", + "Datsun 510 (sw): 60.09%\n", + "Toyota Corolla Mark II (sw): 49.36%\n", + "Dodge Colt (sw): 60.09%\n", + "Toyota Corolla 1600 (sw): 57.94%\n", + "Buick Century 350: 27.90%\n", + "AMC Matador: 30.04%\n", + "Chevrolet Malibu: 27.90%\n", + "Ford Gran Torino: 30.04%\n", + "Dodge Coronet Custom: 32.19%\n", + "Mercury Marquis Brougham: 25.75%\n", + "Chevrolet Caprice Classic: 27.90%\n", + "Ford LTD: 27.90%\n", + "Plymouth Fury Gran Sedan: 30.04%\n", + "Chrysler New Yorker Brougham: 27.90%\n", + "Buick Electra 225 Custom: 25.75%\n", + "AMC Ambassador Brougham: 27.90%\n", + "Plymouth Valiant: 38.63%\n", + "Chevrolet Nova Custom: 34.33%\n", + "AMC Hornet: 38.63%\n", + "Ford Maverick: 38.63%\n", + "Plymouth Duster: 49.36%\n", + "Volkswagen Super Beetle: 55.79%\n", + "Chevrolet Impala: 23.61%\n", + "Ford Country: 25.75%\n", + "Plymouth Custom Suburb: 27.90%\n", + "Oldsmobile Vista Cruiser: 25.75%\n", + "AMC Gremlin: 38.63%\n", + "Toyota Camry: 42.92%\n", + "Chevrolet Vega: 45.06%\n", + "Datsun 610: 47.21%\n", + "Mazda RX3: 38.63%\n", + "Ford Pinto: 40.77%\n", + "Mercury Capri v6: 45.06%\n", + "Fiat 124 Sport Coupe: 55.79%\n", + "Chevrolet Monte Carlo S: 32.19%\n", + "Pontiac Grand Prix: 34.33%\n", + "Fiat 128: 62.23%\n", + "Opel Manta: 51.50%\n", + "Audi 100LS: 42.92%\n", + "Volvo 144ea: 40.77%\n", + "Dodge Dart Custom: 32.19%\n", + "Saab 99le: 51.50%\n", + "Toyota Mark II: 42.92%\n", + "Oldsmobile Omega: 23.61%\n", + "Plymouth Duster: 42.92%\n", + "Ford Maverick: 45.06%\n", + "AMC Hornet: 40.77%\n", + "Chevrolet Nova: 32.19%\n", + "Datsun B210: 66.52%\n", + "Ford Pinto: 55.79%\n", + "Toyota Corolla 1200: 68.67%\n", + "Chevrolet Vega: 53.65%\n", + "Chevrolet Chevelle Malibu Classic: 34.33%\n", + "AMC Matador: 34.33%\n", + "Plymouth Satellite Sebring: 38.63%\n", + "Ford Gran Torino: 34.33%\n", + "Buick Century Luxus (sw): 27.90%\n", + "Dodge Coronet Custom (sw): 30.04%\n", + "Ford Gran Torino (sw): 30.04%\n", + "AMC Matador (sw): 30.04%\n", + "Audi Fox: 62.23%\n", + "Volkswagen Dasher: 55.79%\n", + "Opel Manta: 55.79%\n", + "Toyota Corolla: 66.52%\n", + "Datsun 710: 68.67%\n", + "Dodge Colt: 60.09%\n", + "Fiat 128: 51.50%\n", + "Fiat 124 TC: 55.79%\n", + "Honda Civic: 51.50%\n", + "Subaru: 55.79%\n", + "Fiat x1.9: 66.52%\n", + "Plymouth Valiant Custom: 40.77%\n", + "Chevrolet Nova: 38.63%\n", + "Mercury Monarch: 32.19%\n", + "Ford Maverick: 32.19%\n", + "Pontiac Catalina: 34.33%\n", + "Chevrolet Bel Air: 32.19%\n", + "Plymouth Grand Fury: 34.33%\n", + "Ford LTD: 30.04%\n", + "Buick Century: 36.48%\n", + "Chevrolete Chevelle Malibu: 34.33%\n", + "AMC Matador: 32.19%\n", + "Plymouth Fury: 38.63%\n", + "Buick Skyhawk: 45.06%\n", + "Chevrolet Monza 2+2: 42.92%\n", + "Ford Mustang II: 27.90%\n", + "Toyota Corolla: 62.23%\n", + "Ford Pinto: 49.36%\n", + "AMC Gremlin: 42.92%\n", + "Pontiac Astro: 49.36%\n", + "Toyota Corolla: 51.50%\n", + "Volkswagen Dasher: 53.65%\n", + "Datsun 710: 51.50%\n", + "Ford Pinto: 38.63%\n", + "Volkswagen Rabbit: 62.23%\n", + "AMC Pacer: 40.77%\n", + "Audi 100LS: 49.36%\n", + "Peugeot 504: 49.36%\n", + "Volvo 244DL: 47.21%\n", + "Saab 99LE: 53.65%\n", + "Honda Civic CVCC: 70.82%\n", + "Fiat 131: 60.09%\n", + "Opel 1900: 53.65%\n", + "Capri ii: 53.65%\n", + "Dodge Colt: 55.79%\n", + "Renault 12tl: 57.94%\n", + "Chevrolet Chevelle Malibu Classic: 37.55%\n", + "Dodge Coronet Brougham: 34.33%\n", + "AMC Matador: 33.26%\n", + "Ford Gran Torino: 31.12%\n", + "Plymouth Valiant: 47.21%\n", + "Chevrolet Nova: 47.21%\n", + "Ford Maverick: 51.50%\n", + "AMC Hornet: 48.28%\n", + "Chevrolet Chevette: 62.23%\n", + "Chevrolet Woody: 52.58%\n", + "Volkswagen Rabbit: 62.23%\n", + "Honda Civic: 70.82%\n", + "Dodge Aspen SE: 42.92%\n", + "Ford Grenada ghia: 38.63%\n", + "Pontiac Ventura SJ: 39.70%\n", + "AMC Pacer d/l: 37.55%\n", + "Volkswagen Rabbit: 63.30%\n", + "Datsun B-210: 68.67%\n", + "Toyota Corolla: 60.09%\n", + "Ford Pinto: 56.87%\n", + "Volvo 245: 42.92%\n", + "Plymouth Volare Premier v8: 27.90%\n", + "Peugeot 504: 40.77%\n", + "Toyota Mark II: 40.77%\n", + "Mercedes-Benz 280s: 35.41%\n", + "Cadillac Seville: 35.41%\n", + "Chevrolet C10: 27.90%\n", + "Ford F108: 27.90%\n", + "Dodge D100: 27.90%\n", + "Honda Accord CVCC: 67.60%\n", + "Buick Opel Isuzu Deluxe: 64.38%\n", + "Renault 5 GTL: 77.25%\n", + "Plymouth Arrow GS: 54.72%\n", + "Datsun F-10 Hatchback: 71.89%\n", + "Chevrolet Caprice Classic: 37.55%\n", + "Oldsmobile Cutlass Supreme: 36.48%\n", + "Dodge Monaco Brougham: 33.26%\n", + "Mercury Cougar Brougham: 32.19%\n", + "Chevrolet Concours: 37.55%\n", + "Buick Skylark: 43.99%\n", + "Plymouth Volare Custom: 40.77%\n", + "Ford Grenada: 39.70%\n", + "Pontiac Grand Prix LJ: 34.33%\n", + "Chevrolet Monte Carlo Landau: 33.26%\n", + "Chrysler Cordoba: 33.26%\n", + "Ford Thunderbird: 34.33%\n", + "Volkswagen Rabbit Custom: 62.23%\n", + "Pontiac Sunbird Coupe: 52.58%\n", + "Toyota Corolla Liftback: 55.79%\n", + "Ford Mustang II 2+2: 54.72%\n", + "Chevrolet Chevette: 65.45%\n", + "Dodge Colt m/m: 71.89%\n", + "Subaru DL: 64.38%\n", + "Volkswagen Dasher: 65.45%\n", + "Datsun 810: 47.21%\n", + "BMW 320i: 46.14%\n", + "Mazda RX-4: 46.14%\n", + "Volkswagen Rabbit Custom Diesel: 92.49%\n", + "Ford Fiesta: 77.47%\n", + "Mazda GLC Deluxe: 70.39%\n", + "Datsun B210 GX: 84.55%\n", + "Honda Civic CVCC: 77.47%\n", + "Oldsmobile Cutlass Salon Brougham: 42.70%\n", + "Dodge Diplomat: 41.63%\n", + "Mercury Monarch ghia: 43.35%\n", + "Pontiac Phoenix LJ: 41.20%\n", + "Chevrolet Malibu: 43.99%\n", + "Ford Fairmont (auto): 43.35%\n", + "Ford Fairmont (man): 53.86%\n", + "Plymouth Volare: 43.99%\n", + "AMC Concord: 41.63%\n", + "Buick Century Special: 44.21%\n", + "Mercury Zephyr: 44.64%\n", + "Dodge Aspen: 39.91%\n", + "AMC Concord d/l: 38.84%\n", + "Chevrolet Monte Carlo Landau: 41.20%\n", + "Buick Regal Sport Coupe (turbo): 37.98%\n", + "Ford Futura: 38.84%\n", + "Dodge Magnum XE: 37.55%\n", + "Chevrolet Chevette: 64.38%\n", + "Toyota Corolla: 59.01%\n", + "Datsun 510: 58.37%\n", + "Dodge Omni: 66.31%\n", + "Toyota Celica GT Liftback: 45.28%\n", + "Plymouth Sapporo: 49.79%\n", + "Oldsmobile Starfire SX: 51.07%\n", + "Datsun 200-SX: 51.29%\n", + "Audi 5000: 43.56%\n", + "Volvo 264gl: 36.48%\n", + "Saab 99gle: 46.35%\n", + "Peugeot 604sl: 34.76%\n", + "Volkswagen Scirocco: 67.60%\n", + "Honda Accord LX: 63.30%\n", + "Pontiac Lemans V6: 46.14%\n", + "Mercury Zephyr 6: 42.49%\n", + "Ford Fairmont 4: 47.85%\n", + "AMC Concord DL 6: 43.35%\n", + "Dodge Aspen 6: 44.21%\n", + "Chevrolet Caprice Classic: 36.48%\n", + "Ford LTD Landau: 37.77%\n", + "Mercury Grand Marquis: 35.41%\n", + "Dodge St. Regis: 39.06%\n", + "Buick Estate Wagon (sw): 36.27%\n", + "Ford Country Squire (sw): 33.26%\n", + "Chevrolet Malibu Classic (sw): 41.20%\n", + "Chrysler Lebaron Town @ Country (sw): 39.70%\n", + "Volkswagen Rabbit Custom: 68.45%\n", + "Mazda GLC Deluxe: 73.18%\n", + "Dodge Colt Hatchback Custom: 76.61%\n", + "AMC Spirit DL: 58.80%\n", + "Mercedes Benz 300d: 54.51%\n", + "Cadillac Eldorado: 49.36%\n", + "Peugeot 504: 58.37%\n", + "Oldsmobile Cutlass Salon Brougham: 51.29%\n", + "Plymouth Horizon: 73.39%\n", + "Plymouth Horizon TC3: 74.03%\n", + "Datsun 210: 68.24%\n", + "Fiat Strada Custom: 80.04%\n", + "Buick Skylark Limited: 60.94%\n", + "Chevrolet Citation: 61.80%\n", + "Oldsmobile Omega Brougham: 57.51%\n", + "Pontiac Phoenix: 71.89%\n", + "Volkswagen Rabbit: 89.06%\n", + "Toyota Corolla Tercel: 81.76%\n", + "Chevrolet Chevette: 68.88%\n", + "Datsun 310: 79.83%\n", + "Chevrolet Citation: 60.09%\n", + "Ford Fairmont: 56.65%\n", + "AMC Concord: 52.15%\n", + "Dodge Aspen: 40.99%\n", + "Audi 4000: 73.61%\n", + "Toyota Corolla Liftback: 63.95%\n", + "Mazda 626: 67.17%\n", + "Datsun 510 Hatchback: 79.40%\n", + "Toyota Corolla: 69.10%\n", + "Mazda GLC: 100.00%\n", + "Dodge Colt: 59.87%\n", + "Datsun 210: 87.55%\n", + "Volkswagen Rabbit C (Diesel): 95.06%\n", + "Volkswagen Dasher (diesel): 93.13%\n", + "Audi 5000s (diesel): 78.11%\n", + "Mercedes-Benz 240d: 64.38%\n", + "Honda Civic 1500 gl: 95.71%\n", + "Renault Lecar Deluxe: 87.77%\n", + "Subaru DL: 72.53%\n", + "Volkswagen Rabbit: 63.95%\n", + "Datsun 280-ZX: 70.17%\n", + "Mazda RX-7 GS: 50.86%\n", + "Triumph TR7 Coupe: 75.11%\n", + "Ford Mustang Cobra: 50.64%\n", + "Honda Accord: 69.53%\n", + "Plymouth Reliant: 58.37%\n", + "Buick Skylark: 57.08%\n", + "Dodge Aries Wagon (sw): 55.36%\n", + "Chevrolet Citation: 50.43%\n", + "Plymouth Reliant: 64.38%\n", + "Toyota Starlet: 83.91%\n", + "Plymouth Champ: 83.69%\n", + "Honda Civic 1300: 75.32%\n", + "Subaru: 69.31%\n", + "Datsun 210 MPG: 79.40%\n", + "Toyota Tercel: 80.90%\n", + "Mazda GLC 4: 73.18%\n", + "Plymouth Horizon 4: 74.46%\n", + "Ford Escort 4W: 73.82%\n", + "Ford Escort 2H: 64.16%\n", + "Volkswagen Jetta: 70.82%\n", + "Renault 18i: 74.03%\n", + "Honda Prelude: 72.32%\n", + "Toyota Corolla: 69.53%\n", + "Datsun 200SX: 70.60%\n", + "Mazda 626: 67.81%\n", + "Peugeot 505s Turbo Diesel: 60.30%\n", + "Saab 900s: 0.00%\n", + "Volvo Diesel: 65.88%\n", + "Toyota Cressida: 54.51%\n", + "Datsun 810 Maxima: 51.93%\n", + "Buick Century: 48.07%\n", + "Oldsmobile Cutlass LS: 57.08%\n", + "Ford Grenada gl: 43.35%\n", + "Chrysler Lebaron Salon: 37.77%\n", + "Chevrolet Cavalier: 60.09%\n", + "Chevrolet Cavalier Wagon: 57.94%\n", + "Chevrolet Cavalier 2-door: 72.96%\n", + "Pontiac J2000 SE Hatchback: 66.52%\n", + "Dodge Aries SE: 62.23%\n", + "Pontiac Phoenix: 57.94%\n", + "Ford Fairmont Futura: 51.50%\n", + "AMC Concord DL: 49.36%\n", + "Volkswagen Rabbit l: 77.25%\n", + "Mazda GLC Custom l: 79.40%\n", + "Mazda GLC Custom: 66.52%\n", + "Plymouth Horizon Miser: 81.55%\n", + "Mercury Lynx l: 77.25%\n", + "Nissan Stanza XE: 77.25%\n", + "Honda Accord: 77.25%\n", + "Toyota Corolla: 72.96%\n", + "Honda Civic: 81.55%\n", + "Honda Civic (auto): 68.67%\n", + "Datsun 310 GX: 81.55%\n", + "Buick Century Limited: 53.65%\n", + "Oldsmobile Cutlass Ciera (diesel): 81.55%\n", + "Chrysler Lebaron Medallion: 55.79%\n", + "Ford Grenada l: 47.21%\n", + "Toyota Celica GT: 68.67%\n", + "Dodge Charger 2.2: 77.25%\n", + "Chevrolet Camaro: 57.94%\n", + "Ford Mustang GL: 57.94%\n", + "Volkswagen Pickup: 94.42%\n", + "Dodge Rampage: 68.67%\n", + "Ford Ranger: 60.09%\n", + "Chevy S-10: 66.52%\n" + ] + } + ], + "source": [ + "max_ = max_mpg(data)\n", + "list_data(data, max_)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "collapsed": true + }, + "source": [ + "We may even write functions that need to iterate more than once over an iterable. For example:" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def list_data(data):\n", + " max_mpg = 0\n", + " for row in data:\n", + " _, mpg = parse_data_row(row)\n", + " if mpg > max_mpg:\n", + " max_mpg = mpg\n", + " \n", + " for row in data:\n", + " car, mpg = parse_data_row(row)\n", + " mpg_perc = mpg / max_mpg * 100\n", + " print(f'{car}: {mpg_perc:.2f}%')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "But this will not work if we pass an iterator as the argument:" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "with open('cars.csv') as f:\n", + " next(f)\n", + " next(f)\n", + " list_data(f)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We might want to be more defensive about this in our function, either by raising an exception if the argument is an iterator, or making an iterable from the iterator:" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def list_data(data):\n", + " if iter(data) is data:\n", + " raise ValueError('data cannot be an iterator.')\n", + " max_mpg = 0\n", + " for row in data:\n", + " _, mpg = parse_data_row(row)\n", + " if mpg > max_mpg:\n", + " max_mpg = mpg\n", + " \n", + " for row in data:\n", + " car, mpg = parse_data_row(row)\n", + " mpg_perc = mpg / max_mpg * 100\n", + " print(f'{car}: {mpg_perc:.2f}%')" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "ename": "ValueError", + "evalue": "data cannot be an iterator.", + "output_type": "error", + "traceback": [ + "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[1;31mValueError\u001b[0m Traceback (most recent call last)", + "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m()\u001b[0m\n\u001b[0;32m 2\u001b[0m \u001b[0mnext\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mf\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 3\u001b[0m \u001b[0mnext\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mf\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 4\u001b[1;33m \u001b[0mlist_data\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mf\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[1;32m\u001b[0m in \u001b[0;36mlist_data\u001b[1;34m(data)\u001b[0m\n\u001b[0;32m 1\u001b[0m \u001b[1;32mdef\u001b[0m \u001b[0mlist_data\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mdata\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 2\u001b[0m \u001b[1;32mif\u001b[0m \u001b[0miter\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mdata\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;32mis\u001b[0m \u001b[0mdata\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 3\u001b[1;33m \u001b[1;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'data cannot be an iterator.'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 4\u001b[0m \u001b[0mmax_mpg\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;36m0\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 5\u001b[0m \u001b[1;32mfor\u001b[0m \u001b[0mrow\u001b[0m \u001b[1;32min\u001b[0m \u001b[0mdata\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", + "\u001b[1;31mValueError\u001b[0m: data cannot be an iterator." + ] + } + ], + "source": [ + "with open('cars.csv') as f:\n", + " next(f)\n", + " next(f)\n", + " list_data(f)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "or this way:" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def list_data(data):\n", + " if iter(data) is data:\n", + " data = list(data)\n", + " \n", + " max_mpg = 0\n", + " for row in data:\n", + " _, mpg = parse_data_row(row)\n", + " if mpg > max_mpg:\n", + " max_mpg = mpg\n", + " \n", + " for row in data:\n", + " car, mpg = parse_data_row(row)\n", + " mpg_perc = mpg / max_mpg * 100\n", + " print(f'{car}: {mpg_perc:.2f}%')" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Chevrolet Chevelle Malibu: 38.63%\n", + "Buick Skylark 320: 32.19%\n", + "Plymouth Satellite: 38.63%\n", + "AMC Rebel SST: 34.33%\n", + "Ford Torino: 36.48%\n", + "Ford Galaxie 500: 32.19%\n", + "Chevrolet Impala: 30.04%\n", + "Plymouth Fury iii: 30.04%\n", + "Pontiac Catalina: 30.04%\n", + "AMC Ambassador DPL: 32.19%\n", + "Citroen DS-21 Pallas: 0.00%\n", + "Chevrolet Chevelle Concours (sw): 0.00%\n", + "Ford Torino (sw): 0.00%\n", + "Plymouth Satellite (sw): 0.00%\n", + "AMC Rebel SST (sw): 0.00%\n", + "Dodge Challenger SE: 32.19%\n", + "Plymouth 'Cuda 340: 30.04%\n", + "Ford Mustang Boss 302: 0.00%\n", + "Chevrolet Monte Carlo: 32.19%\n", + "Buick Estate Wagon (sw): 30.04%\n", + "Toyota Corolla Mark ii: 51.50%\n", + "Plymouth Duster: 47.21%\n", + "AMC Hornet: 38.63%\n", + "Ford Maverick: 45.06%\n", + "Datsun PL510: 57.94%\n", + "Volkswagen 1131 Deluxe Sedan: 55.79%\n", + "Peugeot 504: 53.65%\n", + "Audi 100 LS: 51.50%\n", + "Saab 99e: 53.65%\n", + "BMW 2002: 55.79%\n", + "AMC Gremlin: 45.06%\n", + "Ford F250: 21.46%\n", + "Chevy C20: 21.46%\n", + "Dodge D200: 23.61%\n", + "Hi 1200D: 19.31%\n", + "Datsun PL510: 57.94%\n", + "Chevrolet Vega 2300: 60.09%\n", + "Toyota Corolla: 53.65%\n", + "Ford Pinto: 53.65%\n", + "Volkswagen Super Beetle 117: 0.00%\n", + "AMC Gremlin: 40.77%\n", + "Plymouth Satellite Custom: 34.33%\n", + "Chevrolet Chevelle Malibu: 36.48%\n", + "Ford Torino 500: 40.77%\n", + "AMC Matador: 38.63%\n", + "Chevrolet Impala: 30.04%\n", + "Pontiac Catalina Brougham: 30.04%\n", + "Ford Galaxie 500: 30.04%\n", + "Plymouth Fury iii: 30.04%\n", + "Dodge Monaco (sw): 25.75%\n", + "Ford Country Squire (sw): 27.90%\n", + "Pontiac Safari (sw): 27.90%\n", + "AMC Hornet Sportabout (sw): 38.63%\n", + "Chevrolet Vega (sw): 47.21%\n", + "Pontiac Firebird: 40.77%\n", + "Ford Mustang: 38.63%\n", + "Mercury Capri 2000: 49.36%\n", + "Opel 1900: 60.09%\n", + "Peugeot 304: 64.38%\n", + "Fiat 124B: 64.38%\n", + "Toyota Corolla 1200: 66.52%\n", + "Datsun 1200: 75.11%\n", + "Volkswagen Model 111: 57.94%\n", + "Plymouth Cricket: 55.79%\n", + "Toyota Corolla Hardtop: 51.50%\n", + "Dodge Colt Hardtop: 53.65%\n", + "Volkswagen Type 3: 49.36%\n", + "Chevrolet Vega: 42.92%\n", + "Ford Pinto Runabout: 45.06%\n", + "Chevrolet Impala: 27.90%\n", + "Pontiac Catalina: 30.04%\n", + "Plymouth Fury III: 32.19%\n", + "Ford Galaxie 500: 30.04%\n", + "AMC Ambassador SST: 36.48%\n", + "Mercury Marquis: 23.61%\n", + "Buick LeSabre Custom: 27.90%\n", + "Oldsmobile Delta 88 Royale: 25.75%\n", + "Chrysler Newport Royal: 27.90%\n", + "Mazda RX2 Coupe: 40.77%\n", + "AMC Matador (sw): 32.19%\n", + "Chevrolet Chevelle Concours (sw): 27.90%\n", + "Ford Gran Torino (sw): 27.90%\n", + "Plymouth Satellite Custom (sw): 30.04%\n", + "Volvo 145e (sw): 38.63%\n", + "Volkswagen 411 (sw): 47.21%\n", + "Peugeot 504 (sw): 45.06%\n", + "Renault 12 (sw): 55.79%\n", + "Ford Pinto (sw): 47.21%\n", + "Datsun 510 (sw): 60.09%\n", + "Toyota Corolla Mark II (sw): 49.36%\n", + "Dodge Colt (sw): 60.09%\n", + "Toyota Corolla 1600 (sw): 57.94%\n", + "Buick Century 350: 27.90%\n", + "AMC Matador: 30.04%\n", + "Chevrolet Malibu: 27.90%\n", + "Ford Gran Torino: 30.04%\n", + "Dodge Coronet Custom: 32.19%\n", + "Mercury Marquis Brougham: 25.75%\n", + "Chevrolet Caprice Classic: 27.90%\n", + "Ford LTD: 27.90%\n", + "Plymouth Fury Gran Sedan: 30.04%\n", + "Chrysler New Yorker Brougham: 27.90%\n", + "Buick Electra 225 Custom: 25.75%\n", + "AMC Ambassador Brougham: 27.90%\n", + "Plymouth Valiant: 38.63%\n", + "Chevrolet Nova Custom: 34.33%\n", + "AMC Hornet: 38.63%\n", + "Ford Maverick: 38.63%\n", + "Plymouth Duster: 49.36%\n", + "Volkswagen Super Beetle: 55.79%\n", + "Chevrolet Impala: 23.61%\n", + "Ford Country: 25.75%\n", + "Plymouth Custom Suburb: 27.90%\n", + "Oldsmobile Vista Cruiser: 25.75%\n", + "AMC Gremlin: 38.63%\n", + "Toyota Camry: 42.92%\n", + "Chevrolet Vega: 45.06%\n", + "Datsun 610: 47.21%\n", + "Mazda RX3: 38.63%\n", + "Ford Pinto: 40.77%\n", + "Mercury Capri v6: 45.06%\n", + "Fiat 124 Sport Coupe: 55.79%\n", + "Chevrolet Monte Carlo S: 32.19%\n", + "Pontiac Grand Prix: 34.33%\n", + "Fiat 128: 62.23%\n", + "Opel Manta: 51.50%\n", + "Audi 100LS: 42.92%\n", + "Volvo 144ea: 40.77%\n", + "Dodge Dart Custom: 32.19%\n", + "Saab 99le: 51.50%\n", + "Toyota Mark II: 42.92%\n", + "Oldsmobile Omega: 23.61%\n", + "Plymouth Duster: 42.92%\n", + "Ford Maverick: 45.06%\n", + "AMC Hornet: 40.77%\n", + "Chevrolet Nova: 32.19%\n", + "Datsun B210: 66.52%\n", + "Ford Pinto: 55.79%\n", + "Toyota Corolla 1200: 68.67%\n", + "Chevrolet Vega: 53.65%\n", + "Chevrolet Chevelle Malibu Classic: 34.33%\n", + "AMC Matador: 34.33%\n", + "Plymouth Satellite Sebring: 38.63%\n", + "Ford Gran Torino: 34.33%\n", + "Buick Century Luxus (sw): 27.90%\n", + "Dodge Coronet Custom (sw): 30.04%\n", + "Ford Gran Torino (sw): 30.04%\n", + "AMC Matador (sw): 30.04%\n", + "Audi Fox: 62.23%\n", + "Volkswagen Dasher: 55.79%\n", + "Opel Manta: 55.79%\n", + "Toyota Corolla: 66.52%\n", + "Datsun 710: 68.67%\n", + "Dodge Colt: 60.09%\n", + "Fiat 128: 51.50%\n", + "Fiat 124 TC: 55.79%\n", + "Honda Civic: 51.50%\n", + "Subaru: 55.79%\n", + "Fiat x1.9: 66.52%\n", + "Plymouth Valiant Custom: 40.77%\n", + "Chevrolet Nova: 38.63%\n", + "Mercury Monarch: 32.19%\n", + "Ford Maverick: 32.19%\n", + "Pontiac Catalina: 34.33%\n", + "Chevrolet Bel Air: 32.19%\n", + "Plymouth Grand Fury: 34.33%\n", + "Ford LTD: 30.04%\n", + "Buick Century: 36.48%\n", + "Chevrolete Chevelle Malibu: 34.33%\n", + "AMC Matador: 32.19%\n", + "Plymouth Fury: 38.63%\n", + "Buick Skyhawk: 45.06%\n", + "Chevrolet Monza 2+2: 42.92%\n", + "Ford Mustang II: 27.90%\n", + "Toyota Corolla: 62.23%\n", + "Ford Pinto: 49.36%\n", + "AMC Gremlin: 42.92%\n", + "Pontiac Astro: 49.36%\n", + "Toyota Corolla: 51.50%\n", + "Volkswagen Dasher: 53.65%\n", + "Datsun 710: 51.50%\n", + "Ford Pinto: 38.63%\n", + "Volkswagen Rabbit: 62.23%\n", + "AMC Pacer: 40.77%\n", + "Audi 100LS: 49.36%\n", + "Peugeot 504: 49.36%\n", + "Volvo 244DL: 47.21%\n", + "Saab 99LE: 53.65%\n", + "Honda Civic CVCC: 70.82%\n", + "Fiat 131: 60.09%\n", + "Opel 1900: 53.65%\n", + "Capri ii: 53.65%\n", + "Dodge Colt: 55.79%\n", + "Renault 12tl: 57.94%\n", + "Chevrolet Chevelle Malibu Classic: 37.55%\n", + "Dodge Coronet Brougham: 34.33%\n", + "AMC Matador: 33.26%\n", + "Ford Gran Torino: 31.12%\n", + "Plymouth Valiant: 47.21%\n", + "Chevrolet Nova: 47.21%\n", + "Ford Maverick: 51.50%\n", + "AMC Hornet: 48.28%\n", + "Chevrolet Chevette: 62.23%\n", + "Chevrolet Woody: 52.58%\n", + "Volkswagen Rabbit: 62.23%\n", + "Honda Civic: 70.82%\n", + "Dodge Aspen SE: 42.92%\n", + "Ford Grenada ghia: 38.63%\n", + "Pontiac Ventura SJ: 39.70%\n", + "AMC Pacer d/l: 37.55%\n", + "Volkswagen Rabbit: 63.30%\n", + "Datsun B-210: 68.67%\n", + "Toyota Corolla: 60.09%\n", + "Ford Pinto: 56.87%\n", + "Volvo 245: 42.92%\n", + "Plymouth Volare Premier v8: 27.90%\n", + "Peugeot 504: 40.77%\n", + "Toyota Mark II: 40.77%\n", + "Mercedes-Benz 280s: 35.41%\n", + "Cadillac Seville: 35.41%\n", + "Chevrolet C10: 27.90%\n", + "Ford F108: 27.90%\n", + "Dodge D100: 27.90%\n", + "Honda Accord CVCC: 67.60%\n", + "Buick Opel Isuzu Deluxe: 64.38%\n", + "Renault 5 GTL: 77.25%\n", + "Plymouth Arrow GS: 54.72%\n", + "Datsun F-10 Hatchback: 71.89%\n", + "Chevrolet Caprice Classic: 37.55%\n", + "Oldsmobile Cutlass Supreme: 36.48%\n", + "Dodge Monaco Brougham: 33.26%\n", + "Mercury Cougar Brougham: 32.19%\n", + "Chevrolet Concours: 37.55%\n", + "Buick Skylark: 43.99%\n", + "Plymouth Volare Custom: 40.77%\n", + "Ford Grenada: 39.70%\n", + "Pontiac Grand Prix LJ: 34.33%\n", + "Chevrolet Monte Carlo Landau: 33.26%\n", + "Chrysler Cordoba: 33.26%\n", + "Ford Thunderbird: 34.33%\n", + "Volkswagen Rabbit Custom: 62.23%\n", + "Pontiac Sunbird Coupe: 52.58%\n", + "Toyota Corolla Liftback: 55.79%\n", + "Ford Mustang II 2+2: 54.72%\n", + "Chevrolet Chevette: 65.45%\n", + "Dodge Colt m/m: 71.89%\n", + "Subaru DL: 64.38%\n", + "Volkswagen Dasher: 65.45%\n", + "Datsun 810: 47.21%\n", + "BMW 320i: 46.14%\n", + "Mazda RX-4: 46.14%\n", + "Volkswagen Rabbit Custom Diesel: 92.49%\n", + "Ford Fiesta: 77.47%\n", + "Mazda GLC Deluxe: 70.39%\n", + "Datsun B210 GX: 84.55%\n", + "Honda Civic CVCC: 77.47%\n", + "Oldsmobile Cutlass Salon Brougham: 42.70%\n", + "Dodge Diplomat: 41.63%\n", + "Mercury Monarch ghia: 43.35%\n", + "Pontiac Phoenix LJ: 41.20%\n", + "Chevrolet Malibu: 43.99%\n", + "Ford Fairmont (auto): 43.35%\n", + "Ford Fairmont (man): 53.86%\n", + "Plymouth Volare: 43.99%\n", + "AMC Concord: 41.63%\n", + "Buick Century Special: 44.21%\n", + "Mercury Zephyr: 44.64%\n", + "Dodge Aspen: 39.91%\n", + "AMC Concord d/l: 38.84%\n", + "Chevrolet Monte Carlo Landau: 41.20%\n", + "Buick Regal Sport Coupe (turbo): 37.98%\n", + "Ford Futura: 38.84%\n", + "Dodge Magnum XE: 37.55%\n", + "Chevrolet Chevette: 64.38%\n", + "Toyota Corolla: 59.01%\n", + "Datsun 510: 58.37%\n", + "Dodge Omni: 66.31%\n", + "Toyota Celica GT Liftback: 45.28%\n", + "Plymouth Sapporo: 49.79%\n", + "Oldsmobile Starfire SX: 51.07%\n", + "Datsun 200-SX: 51.29%\n", + "Audi 5000: 43.56%\n", + "Volvo 264gl: 36.48%\n", + "Saab 99gle: 46.35%\n", + "Peugeot 604sl: 34.76%\n", + "Volkswagen Scirocco: 67.60%\n", + "Honda Accord LX: 63.30%\n", + "Pontiac Lemans V6: 46.14%\n", + "Mercury Zephyr 6: 42.49%\n", + "Ford Fairmont 4: 47.85%\n", + "AMC Concord DL 6: 43.35%\n", + "Dodge Aspen 6: 44.21%\n", + "Chevrolet Caprice Classic: 36.48%\n", + "Ford LTD Landau: 37.77%\n", + "Mercury Grand Marquis: 35.41%\n", + "Dodge St. Regis: 39.06%\n", + "Buick Estate Wagon (sw): 36.27%\n", + "Ford Country Squire (sw): 33.26%\n", + "Chevrolet Malibu Classic (sw): 41.20%\n", + "Chrysler Lebaron Town @ Country (sw): 39.70%\n", + "Volkswagen Rabbit Custom: 68.45%\n", + "Mazda GLC Deluxe: 73.18%\n", + "Dodge Colt Hatchback Custom: 76.61%\n", + "AMC Spirit DL: 58.80%\n", + "Mercedes Benz 300d: 54.51%\n", + "Cadillac Eldorado: 49.36%\n", + "Peugeot 504: 58.37%\n", + "Oldsmobile Cutlass Salon Brougham: 51.29%\n", + "Plymouth Horizon: 73.39%\n", + "Plymouth Horizon TC3: 74.03%\n", + "Datsun 210: 68.24%\n", + "Fiat Strada Custom: 80.04%\n", + "Buick Skylark Limited: 60.94%\n", + "Chevrolet Citation: 61.80%\n", + "Oldsmobile Omega Brougham: 57.51%\n", + "Pontiac Phoenix: 71.89%\n", + "Volkswagen Rabbit: 89.06%\n", + "Toyota Corolla Tercel: 81.76%\n", + "Chevrolet Chevette: 68.88%\n", + "Datsun 310: 79.83%\n", + "Chevrolet Citation: 60.09%\n", + "Ford Fairmont: 56.65%\n", + "AMC Concord: 52.15%\n", + "Dodge Aspen: 40.99%\n", + "Audi 4000: 73.61%\n", + "Toyota Corolla Liftback: 63.95%\n", + "Mazda 626: 67.17%\n", + "Datsun 510 Hatchback: 79.40%\n", + "Toyota Corolla: 69.10%\n", + "Mazda GLC: 100.00%\n", + "Dodge Colt: 59.87%\n", + "Datsun 210: 87.55%\n", + "Volkswagen Rabbit C (Diesel): 95.06%\n", + "Volkswagen Dasher (diesel): 93.13%\n", + "Audi 5000s (diesel): 78.11%\n", + "Mercedes-Benz 240d: 64.38%\n", + "Honda Civic 1500 gl: 95.71%\n", + "Renault Lecar Deluxe: 87.77%\n", + "Subaru DL: 72.53%\n", + "Volkswagen Rabbit: 63.95%\n", + "Datsun 280-ZX: 70.17%\n", + "Mazda RX-7 GS: 50.86%\n", + "Triumph TR7 Coupe: 75.11%\n", + "Ford Mustang Cobra: 50.64%\n", + "Honda Accord: 69.53%\n", + "Plymouth Reliant: 58.37%\n", + "Buick Skylark: 57.08%\n", + "Dodge Aries Wagon (sw): 55.36%\n", + "Chevrolet Citation: 50.43%\n", + "Plymouth Reliant: 64.38%\n", + "Toyota Starlet: 83.91%\n", + "Plymouth Champ: 83.69%\n", + "Honda Civic 1300: 75.32%\n", + "Subaru: 69.31%\n", + "Datsun 210 MPG: 79.40%\n", + "Toyota Tercel: 80.90%\n", + "Mazda GLC 4: 73.18%\n", + "Plymouth Horizon 4: 74.46%\n", + "Ford Escort 4W: 73.82%\n", + "Ford Escort 2H: 64.16%\n", + "Volkswagen Jetta: 70.82%\n", + "Renault 18i: 74.03%\n", + "Honda Prelude: 72.32%\n", + "Toyota Corolla: 69.53%\n", + "Datsun 200SX: 70.60%\n", + "Mazda 626: 67.81%\n", + "Peugeot 505s Turbo Diesel: 60.30%\n", + "Saab 900s: 0.00%\n", + "Volvo Diesel: 65.88%\n", + "Toyota Cressida: 54.51%\n", + "Datsun 810 Maxima: 51.93%\n", + "Buick Century: 48.07%\n", + "Oldsmobile Cutlass LS: 57.08%\n", + "Ford Grenada gl: 43.35%\n", + "Chrysler Lebaron Salon: 37.77%\n", + "Chevrolet Cavalier: 60.09%\n", + "Chevrolet Cavalier Wagon: 57.94%\n", + "Chevrolet Cavalier 2-door: 72.96%\n", + "Pontiac J2000 SE Hatchback: 66.52%\n", + "Dodge Aries SE: 62.23%\n", + "Pontiac Phoenix: 57.94%\n", + "Ford Fairmont Futura: 51.50%\n", + "AMC Concord DL: 49.36%\n", + "Volkswagen Rabbit l: 77.25%\n", + "Mazda GLC Custom l: 79.40%\n", + "Mazda GLC Custom: 66.52%\n", + "Plymouth Horizon Miser: 81.55%\n", + "Mercury Lynx l: 77.25%\n", + "Nissan Stanza XE: 77.25%\n", + "Honda Accord: 77.25%\n", + "Toyota Corolla: 72.96%\n", + "Honda Civic: 81.55%\n", + "Honda Civic (auto): 68.67%\n", + "Datsun 310 GX: 81.55%\n", + "Buick Century Limited: 53.65%\n", + "Oldsmobile Cutlass Ciera (diesel): 81.55%\n", + "Chrysler Lebaron Medallion: 55.79%\n", + "Ford Grenada l: 47.21%\n", + "Toyota Celica GT: 68.67%\n", + "Dodge Charger 2.2: 77.25%\n", + "Chevrolet Camaro: 57.94%\n", + "Ford Mustang GL: 57.94%\n", + "Volkswagen Pickup: 94.42%\n", + "Dodge Rampage: 68.67%\n", + "Ford Ranger: 60.09%\n", + "Chevy S-10: 66.52%\n" + ] + } + ], + "source": [ + "with open('cars.csv') as f:\n", + " next(f)\n", + " next(f)\n", + " list_data(f)" + ] + } + ], + "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.6.2" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/Part 2/Section 04 - Iterables and Iterators/13 - Caveat Using Iterators for Function Arguments.tscproj b/Part 2/Section 04 - Iterables and Iterators/13 - Caveat Using Iterators for Function Arguments.tscproj deleted file mode 100644 index 77021f73..00000000 --- a/Part 2/Section 04 - Iterables and Iterators/13 - Caveat Using Iterators for Function Arguments.tscproj +++ /dev/null @@ -1,206 +0,0 @@ -{ - "title" : "", - "description" : "", - "author" : "", - "width" : 1920.0, - "height" : 1080.0, - "version" : "0.5", - "editRate" : 30, - "authoringClientName" : { - "name" : "Camtasia Studio", - "platform" : "Windows", - "version" : "9.0" - }, - "sourceBin" : [ - { - "id" : 1, - "src" : "D:\\fbapt\\Dropbox\\Python Deep Dive\\Part 2\\Section 03 - Iterables\\23 - An Important Caveat Using Iterators as Arguments\\Caveat using Iterators and Function Arguments Rec 05-05-18.trec", - "rect" : [0, 0, 1920, 1080], - "lastMod" : "20180505T233644", - "sourceTracks" : [ - { - "range" : [0, 58946], - "type" : 0, - "editRate" : 30, - "trackRect" : [0, 0, 1920, 1080], - "sampleRate" : 27, - "bitDepth" : 0, - "numChannels" : 0, - "metaData" : "Caveat using Iterators and Function Arguments Rec 05-05-18.trec;" - }, - { - "range" : [0, 86635520], - "type" : 2, - "editRate" : 44100, - "trackRect" : [0, 0, 0, 0], - "sampleRate" : 44100, - "bitDepth" : 16, - "numChannels" : 2, - "metaData" : "" - } - ] - } - ], - "timeline" : { - "id" : 2, - "sceneTrack" : { - "scenes" : [ - { - "duration" : 1963.93333333333, - "title" : "", - "type" : "", - "csml" : { - "tracks" : [ - { - "trackIndex" : 0, - "medias" : [ - { - "id" : 3, - "_type" : "UnifiedMedia", - "video" : - { - "id" : 4, - "_type" : "ScreenVMFile", - "src" : 1, - "trackNumber" : 0, - "attributes" : { - "ident" : "Caveat using Iterators and Function Arguments Rec 05-05-18" - }, - "parameters" : { - "cursorScale" : { - "type" : "double", - "defaultValue" : 1.0, - "interp" : "linr" - }, - "cursorOpacity" : { - "type" : "double", - "defaultValue" : 1.0, - "interp" : "linr" - } - }, - "effects" : [ - - ], - "start" : 0, - "duration" : 58918, - "mediaStart" : 18, - "mediaDuration" : 58918, - "scalar" : 1, - "animationTracks" : { - - } - }, - "audio" : - { - "id" : 5, - "_type" : "AMFile", - "src" : 1, - "trackNumber" : 1, - "attributes" : { - "ident" : "", - "sampleRate" : 44100, - "bitDepth" : 16, - "gain" : 1.0, - "mixToMono" : false - }, - "channelNumber" : "0,1", - "effects" : [ - { - "effectName" : "NoiseReduction", - "category" : "categoryAudioEffects", - "parameters" : { - "denoiserAmount" : 20.0, - "denoiserThreshold" : 0.0, - "noiseProfile" : "cX0sQk3YgEJ0wBtBJ2XSQA33aUCVQ75AymfGQGxquT9h/DVBGMKqQPHAhD9pKhc/BZyHPyOxkz5c\r\noDM+oCNxPrLwKD6GGso9W8i/PhGtiz+fYxQ/VHksP1lsHz+CdjQ+FEjCPs9zMT7qWhs+ZPknPnoK\r\nWz14DkU9VpEGPtR/KD0Sn8099SiRPVN0yj2wb8U9lJ8rPlhPnz3SkjI9xjGAPbk7Xj1CkcA9b0Tr\r\nPVYLzD0rzBw+cf6fPaZ55Twy1N08II4xPDdtTzynrqg8ukaTPFO+KTwEh/U7Mk9YPLaAXD06aeQ8\r\nrB73PDE0YT2k2mI9DUACPSqkAD18swk95zozPfDP8TyPpug9VYqkPHBPVj0qxxc9/MkMPZetAz1r\r\nHYs9iHA2PTv/gz0qs9s8/DfRPeF+9Dy0OTk918AMPeOu9T3CCtA9GxbtPQPAgj0Re18+Bic6PnKj\r\nDD+dwPg+2X28Pt8L6D7wXDg+MCe7PR+TIj1Pcys9wcd5PDD5rT1+u549W5WfPCT2nz2sCEA+9JP7\r\nPMsiJj0Oduk8zmWzPkeIYz194Q49l3iTPRDBhj2X1ro8oNW0PISttjyjvkQ8hpKQPGQpqTw+6qY8\r\nsR9aPK/flDvAe2k8SFUPPf4iaTxNN0E8rvLdO7p3zz2T3yQ9QE90PblzpDyjAwo9kYHDPUJhMD0R\r\nDZI9LQvhPKkt6jz5ua87dwOBPARwsDskH0s9r2IWPKx1Mjz8ifg7Q6HOPNrMnjxneTQ9BdqKPc98\r\nBT0jAUQ9qBF4PTEDgD1lhfk89YVGPCyBCT3j3I88+sNYPByGrzzs5qA8hcBmPcAVuTx5e7M9pLme\r\nPeH0vz34wrk90L2fPZD+OD7gA9g9BvsbPhsYqD1DPxo+R+KZPfBcJz4/rZs9w7mwPZqeuD0JBLg9\r\n87xiPUOUdj06hIs925vDPPoaRT1iWZY9TPmrPaN04D1mBj4+oONfPh5iUD5g1Zs+CIMcPgwrXD4L\r\nqCU+VijyPQe6ID76nlA9opK9PSfzDj4yGm49qlCiPfVrHz1yays9C6L2PLxKYD1Q34A9T8n7Pd06\r\n5D1qWBE+wjIBPvZQBz6X6/k9iWMxPQu4+D2gDOo95tuVPQaPkj0yot88YHjaPSSmMT3VD1U9YO4f\r\nPHaISzy8kQY8c4zWPNgD2Dwpdfo8sCV4PagjTD3XfJ891muGPZw2yz22gt88D+IzPb5AGD15Hcc8\r\nPdjZPIgroDxv7Pg8lb+mPJpqijxczfE8w/rpPCohHj04+sI8GJKUPMzY/Dx/aNI8MBvWPFxEVTzA\r\nTxA9aiYAPbysezwxVMA8MX7iPCLOXTwldic8d9pXPPYZMzzSffw7TkslPES+zDsX+Wc8CkL4O2N5\r\nwjsyqyk7yoRHO0IiyzsKdVM7FE4LPLyWWzyxZ647CS/PO2dafju130s72OGyOnXHkTsxXDE8sQob\r\nPLRI4DtfhRo7BmrjOr7HzzouBTI7p6J7OtUnCDtvdpY6my2tOlSDszr0cb86In8CO0TbRDvO9jE7\r\nxJ6nOiqc5TqZdCo7TxCQO10OqTrk8h87OHf5OuzMqzoMF6Q7pyL0OoGVVzq5bKU620tUO9xMBjuC\r\nq1g6DVYCO8RuCjsUnQU79yqEO/h6YDsvz/Q7/h9/OzbX/DsOolM78abxO0g9XTuBh3s7sQecO4zj\r\nQDuC7TY6LT8KOxD0Mjuj56U6l8bYOg6oeTvSNFs7KJ4kOwa28TtLHI07qTajOzpnHzuvrp47V5na\r\nOoapyjquEAU7RPi6OsJ7MjuzRHw6qimHOsbWxDp8wPY6vKj8Op4t3zp2lA47/LO2O1maYjul3eI6\r\nOHB/OvznCDsBwgA7+EG+OsZghDrZwzc7kWF5Oyx54DpTQ846zoJCOwQ/ATtwVFg7juebO2jSDTxo\r\n3hQ8vBdoO8/zmTsP4J07fDOrO8PWFzyT2/U6VtTVO+nqJzuM5G47ZFabOqtG2zoiGYk6OKuUO3Vt\r\nqjoLvM46odIzO1mPizqYRZQ6jhfOOugvsToWfd07xysfO/h+WTuR6yQ7hkrDOpVItDonmHk6exq2\r\nOx0STDsEDMc7cjtZO8gL8jo4pkM83/jrO4P7aDx5/gU8oHhdO03wMzv3c006GhhgOqUc1jqeb606\r\nXroIO3W+DjpVbRI6baTVOqTCxToAr5Q65ewsOgpx1jpnSa07nvIEO/+xJjscBKs6pN+DO60hDDtx\r\nLo47hSgQO2wZHjt80hE7yBs+Osf5azombEM6yOXdOrvA4TpzYJc65evjOj6u7zo2gyA7ACmHO17a\r\nxTu0iUA8s+aIO7f+xDviwOo7UNAjO646/zqjLqg6OVmMOmqBjTpjnUk7EIGROyK39TpQxcA69g18\r\nOjg4xTtdCco7OxG6O08RvDsxkso6WDj9Ov5yKTtEb/M73Xq+OyQjljrkd6c7UkgfO6AtuToY8pE6\r\n9aE2OwQ6djtQEtA7JaJoO1yAjTqcvyY7cAveOtLpgDvgtyQ87jhQPJKDZzycXnA8pXqtPOyz+TvU\r\nr1s8leAzPKodCDyuijQ7VVavO1yNpjvv9ys64eIpOiLggTot3407yqaOO3f1NzpCL7A677K0O1rS\r\n/Tqje6o6FD+wO/GqGjxIhv47FfvqO4ZXBTypPhc7K38TPNBLajsh4g48XKyOO9y52DqhQMI6evWE\r\nOxaikjpUdHQ7DmWXO4eKgzv4fRE8vArXO64ltTujr0A8YZtQO7FQiTtoiqY7YfKcO6zgXjtCtg08\r\nCti6OpuLPDvWxSc7ZGqJOtxejTqcozQ6jcxhOtPhhDq47286PmCAOkP9RDuDmBg7iMrHOnh5KjvQ\r\nF4k7FA4fOoIbDzsOzd46H14UO0pYjjq7lIs6BfqzOrZI7Dr4B2U7ExROOw4OmDpuY3Y66bRXOuf+\r\nYTo8P8M6zQMHO90hODtKhoQ6cTCEOvSEkTpBZk86AG20Oj9WRTq91+A6v6OIOg02uToHnvU54FYr\r\nOon9bzoI9ag6hY+TOqGApjpQ5A07OJ6wOyXaQjrOGFI6YRcBO06XPTpy1z06vW4IO8JRTTqR3oQ6\r\nQKg6OiE6gzrMq3U64l5POicXCzr7vww7DyADOmB0WToC1Pg6nMhgOpMmcTo66pI6orvoOSZo6znd\r\nC306BrbTOZ6wNjovW6I65lp+OhipZjp4MbI6RSKEOlvLwjo6Peo6/x/ROsGGWzo7MJg6gseLOrFe\r\nczoUreg6fzWPOlFKzTpUw0M6KkWVOtEIHjowXmg6rj2tOgSWQzoPUpY6LjtrOvqIYTpmeXY6G+dI\r\nOpUQlTqZOQM6NP+1OeG9jTqMRmU6KEmgOnQ1PDp/gvE69IlROk+iAjroCxw7eAObOhTJNzqWSss5\r\nsSLmOWHXXjqMuk86avAPOiyczjm6YQs6CmFJOnJLBDobSUA6l6ZxOoxbPzqgwso5YnRhOgbFMTq8\r\nfyg6+MBROr6DvDoUF4s6jpB7OtcfeDpUCK46g9NUOlqOVTr4+Ds6OZMnOrtbJjr5Lf45cqnNOQrP\r\nkzrCxIo645pbOvQaHToCtpM6Y1pbOqJ7LDqC2zc6p6SGOrVOeDqwMDU6Gsm7OXBuDToJgzY6QbUb\r\nOrQJMzpDvAI6lEd2OuZsBzoXlJk6NNAlOqSHjDpYebI6AmWCOnflJTpGFgk6cLiuOlzgDzoiVWA6\r\nnKhdOtuLXjqF2W86ioQvOkS0hTqEZsg6S1qDOmrbizraYbI66JGoOu77jjqNlaY6ahYsOh86hzpT\r\nMnE67nJcOuYTvTrsnAc78bJqOksdOzplrYQ6erXFOpLrbzrTw3E66OIvOtC+FDqk0Dk6wgFNOmIe\r\n4TqyszQ7vj/OOi4XnjpUoW86qMXBOf0LkDoC1wM6tqMdO4AuFTuKwCk6qioGOi6tiDrwRPU6ZWLN\r\nOgw6CTvgCKQ6FyEFOz7YtzpN2Mo6dlspOy41szrRhD86zA6EOvaFUDt7Clg7JFOQOi0oUTthkIo6\r\ngkOoOhhsbDqlCI86ULX3OUQBBDrXZcc5UMYUOnw8IDoO0fE59fl3OWQUfzlk2zo6meoyOyfOeDoz\r\ngEU6yn+9OhOKkzuihao7rrUoOzY/9TpuKds6It25Ovm9mDqJT1Y7kCc4OpyV6DlTC086JIXiOV7e\r\nDDoaw3Q65M50OpSl8DkrKwU6WttPOndzSjtGxjY7Rb7zOuWiJDu4LKU6lc3tOhZBBDvxZCI7Ypeh\r\nO6xjxzq6RSU7tFnSOrSHgDrPRgQ6ek2tOoQ3WjrdCUA6Wi8AOvoa5TnPvbU6sIE2O/K8HTtZFWA6\r\nQvUYOnjVYjs4lj47uS28OeYRADtjQwI6nmRmOl6egTpSbKs62/KSOh2IRTrU6EE6pI3nOZDRDDop\r\nZx06vHc9OkdvTzpF2To6DGyIOuHnSzoeaik6tcCDOmPbKDu7JJA6EjPQOpIPTjqkFp06stdOOiUx\r\nsjrK7is6nViXOizwbDqjvJk59dniOfCN2TlQ8PY5TSFUOvZsXDnclug5C/u/Omvqjzqo+Us6Efic\r\nOjj22zpcRm86AUN8OoFhojqgNXc6Ae6mOtkHozqRYlY6/YZ7OiR5zzmpVwo7ej0AOnGSDzpzapw6\r\nCmk2OgfwHDqbXQM65NlbOtt5gjoPk4M6poKWOrqQ8zoftwk7QTGvOlp2oDrrQCA62bbgOfWr0Dn0\r\nnBM6897+Oe/HrDqSRek5vG5DOnSqhTq03s46vay9OgL8tTpW9Ts6AsS5Ol61bzpLNoM6EN0EO+Tc\r\nsTrHBAc7sA+WOmkbLTofgI86Y9qFOoR7gTpeD8U6jF8MOuX7XDoG3iw6quK3Or0ekzq3l8I5Pxlp\r\nOpdjdTo1RYM69D4IOkkgjjrWYC46VEx6OXZQUTlWeTE6bxItOjL7YTogqwo6fuKROm3iHjrHQMA5\r\nGuU0OkZRpjp4DZE6jZZ+OjuIJjpSyGU6xI6EOtTdWzpkbl06rw2GObl6/zkcCwY6fMLyOY6AUTqv\r\nXcc5GyaVOVWP+DjRM3U4JngTOI56GThM4x44RG4WOHO8BjibfRM43+z+NzCnBzhT+wg4e+oaOK/R\r\nDDjpXg84Lb0OOIHfAjhTnw44GyAGOLN73TdA6gs4dJ8POJNmAjhU+BA4sLAsOMOQADjGYBc4r0gM\r\nOJhyDTgOQRQ4YrQTONcHEziRlRc6bxwQOn0dCzjoCQ04X1MIOJ68CzhAiAs4+DL7NwoqCDjX4Ag4\r\n26wjOGcqCjiRMOQ3IagLONSXDzjVzgQ4YVn5N/xQAjiLxOU3pNIOOBjsDDjCIO0346ECONAmBzh9\r\n6N03yN3yN09tAzi+RRY4vQv4N8/MEjhENAE4m3D+N/VwGDhUtPo3qUgEOHssAjiEzwI4BO0DOGC7\r\nCzjete43cewIOIbY/Tds9AE4jEACOCch6jdD9fU3KQIAOP1w2DdBSP43hKcNOLwb+jd68QI4Anf9\r\nN0B0CDi0qAk4Phr4N37PAzjeCQ04aF0BOEKgEThAHgA4QQUFOBhODDgBeAs4eGYlOACy1DeVczpC\r\nwNCAQtrrGkGH5eNAHwNkQLzvvkDoOclA69y5P9qdNEFZ0KhAvrKFPzTBDj88cIg/qXCTPpDDLz4F\r\n4XQ+iQUlPoco7j2SN78+m+qLP3JzFj9mPCo/5gEdP7G4IT7ekss+mewiPlWAHj6QBC8+inlYPWmX\r\nCD042gU+LEJBPUQpiD3cx449IRKsPToAzT0ANiQ+xhOYPaAlOj2qJV49VMI0PfCosD1Dde09GAHH\r\nPcne/z3zqak9WCUFPchcAz1htkw8EHckPPId4Txgi1k8j1mTPKUgFzz66yA8N99qPWJzAD33K/08\r\nuAQvPRV7Rj2kxCQ9rLEPPbqZAz3oYE49recEPaHT3T0DoY085FUiPYB9ET0oCvc8zw4RPQM9mz1p\r\nXhQ9TmplPdcAwjykfLA9y4oDPW4ODT2jrCs9JtjVPUYj0j0WZfI97AeDPRB1Sz5UNDo+sXMJP3qW\r\n+D4E/bY+1rnhPk83MT6Ve7c9XyoSPemUNj1ycpQ8svTHPdnYmT0afoA852WdPckEVz77GNI8cuMQ\r\nPXvO9TxaN7A+gMFgPfkQHj1Z5I89Z0luPeLTnzzclag8OMWwPOE1PDz4ioc8JQDOPHhGljznB0Y8\r\nkoajO4gARzwN1ec8EtwZPB6JZjwT9ws8+EPWPbVmGT2ViSg9Yy9fPBxPBD2Ihtg9oMFBPW6Mcj1o\r\n/ZU8O93SPJwSpTs9xnc8mwZMPKYKET2Kx887nv4rPAjD1zsJP648fYx1PAcNCj3mr4I9ilnVPPPJ\r\nNT3thok9JwV6Pd0s/jxoSGk8jYMZPc84zDwnrmI8A3O9PFebrTzYdmw99FXvPJi2pz3iALg9lNSq\r\nPUEs1z0PBK49uXdHPgGA7j22nhg+v2qGPS2qEz6b2KE9wvIePoICpj3XX909Yym6PWABvT0hjWU9\r\nZ9lXPcn4ez2d5LY8JbQ2PfsvlT2dwZI9D1IBPvsNNj7Z+lA+/olUPohEjz51EiY+HLZjPhMVFj6S\r\nNc49pO0fPpPFMz3NEKc9JbwLPtIDRD2lN509PhkYPctTJT3DAf08tuNQPTiDbD0vKec91rvgPQ2v\r\nGT71j/09LKQBPkOf8z3WfyY9Oz7mPWC36z3Ov6Q9enmpPT0r/jydaNg9pVjxPDngTj0tEpY8Vp1R\r\nPKU9UTxiHM48//XvPECs4TxIdk49/mMsPWlLnz3UYIQ9gonaPV4Z+jxnfVQ9OukVPXk5ED1VieY8\r\n7OyoPGN/8jwDz2w8qaOOPEhwAz0qbNA8/BAPPeTCozwJIoQ8ZwXbPM4xtTz2l9U8eR9JPP0xDj16\r\nOgI9DF1oPDC4xTyfALA8FB76O99UcDxcWiY8vxIRPI2vrjttNTE8vSe8O40/jzxSLJ87L0/YO0b2\r\nADstzJs6qvfvO6/D3jspB9Q7Pf8XPLDorDupP6E769ErO0TqFDuUu2c7UH1KO3uUSzxhA7A7wBVN\r\nO+GmRjtmIFw7CnNnOjB1MTshPug65NlGOyDwljr8osE6jWsFO8KlaDrfm8I65HhOO4A0ajv+mgs7\r\nSH0cO+hGBDvcPpE7wz24Otp50TrVhLw6MEeDOkCxpjq4Rqw6umaiOvnODztz69A6dkiqOkxBxjo0\r\nPAM7RtCIOlJdyzqEWfQ76ul2OzjOBDy8uMc7yNoKPF/hhDtkVPA7BPVhO5dFijucZVE7lkyfOzZ1\r\n1DpYaWg6wngpO/ZoxTr3GzY7FR4rO16GsDspJe06mYnJOwdGrjtOhB87BDeGO7yJ2TvDRx47xF2I\r\nOvR/DTt2Drc6qlmIOjidvjp0Lbo6pHvLOvDwvjpfPJA6yNIJO+LIbDqUH886XtWZO3gbkTrpgcA6\r\nUSZOO6IyoTr6CGE6G2enOsChBjt4/uI6P8IBOx87BjuMQ7A7N/0uO/J/0TsKNFM7emeqO54LpDvg\r\nawE8w9aJOz0FozsZKBY8sTfEO+G5/DqGArA7E6loO9yflDsw5ww7Fp3WOhCWhjo1s4w7VD6oOr69\r\n6DoJQA07QFVlOiy17Dr4X1U6LV1mO3wAPjvhe4E72pAnOyGgKTv+NN06tAeWOvhXBjujvpY7Esg5\r\nO0+DiDurReE73Q0iO0JzHTwT574705IwPCqJrDsKHFs7pCUoOzh0tzoeL5g6Ah4MOu2zljpAP7I6\r\nePA3OkSe8TpThtY6DdsiO4e2MDr+Vvc6CBvZOnCxZjt/r6U7xCstO8OVzzqz4Z47HloLOwZRHzuQ\r\nhfE6oZsiO6wXPjs14Q86Xjl2OkBVhTo3Ww06xFIrOwGBozqsMhA7BrBWO2MXTTsOR/87Xvw8OycQ\r\nKjxdw4Q7ULu7O3/m2DuMxzU7zDPuOkrHYTohzJ06/5mrOpsoWjuseKM7iObNOvmZ1zqfMoc6mDjQ\r\nOwif1DtCV7A7hM3dO20GqDrg2UM7yDvdOpDRBDzzsok7cvReOi7JhDtYCOg6WkNMOxKByzp675E7\r\nCuxBO3XkwTsNe047dNzbOtJY1josTC86FjC0O/A3Gjym0kc8XECVPC4wSjwwtWU8G47vOx4qgDzH\r\nbAU8XdAMPEZhhTt2OlE7c6yWO7YdoTrEtk06zcILO+0JejsA25E7yIIIOsUQQzvWnP07+cVMOwya\r\nOzpK9Lc7L3yyO6cQBDx1aps7Pp3UOynHjjuMV5E7OO6LOnXrYDud0w87JAzKOhBhSjsI1Ic7LsTN\r\nOjLzFjsI1pI7Pv2AO2zqvTuTri47Tni1O9tDxzvhOus65pSuO/dfsDulSpM7yqqUO9Ck1ztGitQ6\r\naMbtOtL0oDqbDpg6fDY6O3//QTpyJCA7Eb2YOgfrsDrBb5w6yPPBOiRRTzokgjo6Yw0oOyTSgju0\r\nfus60Dv8OtwjEDvzlQM7mpS4OrB3pDpTA9o6+D7xOgrFITuyGAA7s46ZOkUDkjoQglg6fldGOvvU\r\nQzqvn8E6be8vOvupizpcIiw6ihiQOtj4jzphdhE74JsRO1S+bjq4FlI6Tk2FOs7EVzqoHZg6dm6B\r\nOoTM5DqZ0VA6/uX/OuW5jzoe8xw7ZiIbOp55rjqgmZ46BIAEOnWxWTrhya06OMCHOtyHJjuuUXg6\r\nwLiBOuDg4TqxIMA69s85Ol5z9zp3ns862h6cOhcyqDp7Jg46lMKROnAO3zooMFk6DDpIOn+IWjoU\r\nATk6h6oBOmlAWjrukww6JPYTOjTdqTrTxyE7eemOOjGf7TrJMww76yYUO+8+gzrAk2c6la5sOnZ+\r\nCjvO0oU6dtK/OosRsTpP6oM6CbM6OqKbBTocNJs69XTiOZctlzrqYxE6nnN0Oltj9jndvGM68XWI\r\nOsoyfTpnRrU5Q6o8Oq8N4jooW60660EBOrrO8jo+xIM65w2QOsPLVTpVH3Q65FB0OhTFfjo162A6\r\nvWoCOxtCpDolc0w6BRkGOhy/2DodTS463MRFOgGlmjoxJRw6lGmhOmFxUjoUpAM6Vs1wOlhoPTr9\r\n0sE60lOeOjH1NTqKaFI6IVwZOis7kjpuWpg5EouROow/bzpJCnU6afCCOoq/NzrSHcY5t1kEOvlm\r\npzoFd6Y6ftSYOibstToMu6c6Lc+SOkZPojmDCMo6Kv0zOpdPqzrpokI6VoMwOn0ZJzoVxIk6dU02\r\nOlscXTrEgMU5msMAOpnEljkjLUQ6Vy5hOqcNdzo6SpQ6GqQ2OrLB0Tnqpy06/RlaOoSWHzpsENU5\r\nPD5ZOiQAATqQ+TU6hdllOuzpazrfOxM7tJFlOnz1SDrl0G46ue5kOlwalzoMimc6ZmeCOjAuWzpb\r\nmHA6CqYWO0T0lTqOZIY6nhIWOlokijqnQrM6vWJkOmo/ijoncSY6fcLzOdU+NDraqCg6wMriOoAS\r\nLztSR746l2+mOuHxdjqcTdU5d4OFOvGXHjomORI7/ycWO82pLzoEQQI6eQGNOhV/yzqvBc46Kz0H\r\nO1irszpir/s6YsmtOlbtxToEaBs7HFCqOt2DIzr1DYg6ipJDO0F10zoC5Q07yql5O7BjHTr5ZwU7\r\nq7HJOV+GjzovETY6vho4OnOhszkFego6qLczOoOQ/zkIf5U540GHOZ/ZEDr+ej46MPAEO4tO0jqg\r\nV4s6O3GjO9HYuTv5kBM76kMcO6fYlzo8ry47zMgsOxbH+jqXWjk64PToOVAQTTq5Nfg5+fMPOvOY\r\ndzrs92o6bQTfObJvCDqK2FU6MINSO75tMTs+jvU66O4cO9YGqzoOIPA6J6gEO8ixKztKv5s7VJHN\r\nOuDHJDsYL846wCB2Otcj/zkmT6Q6OPZkOtlWPjp4huk5IQvXOahDuDrCMjc7e2ofO3KcUjqFlA46\r\nsdVlO3u8RDtW0b85Xr73OtK9ATquRXU6+pGAOlDCqDr+SpY6bqtPOhtuPDrhAP05f54QOlb3FDos\r\nQTE6BhxbOndxMDpJQIw6wixQOtA3KDoV7YY6kjMpOwbghjrEG9E6+O9JOlXulDpiBD06gqCtOpw3\r\nLjpE+JU6Gm+AOqCBqzmCX9s5cajLOSAW5TmrBm86ISNXOQgi9DnH07U6A4ynOnPvZzpuOJw64P0D\r\nO7LyojovdHE6PAyeOi5LcjpF+aE64t+fOqHQVTpmBHA6zqjTOaZVCTuqePg5JZcMOoawFzqqPKE6\r\nEb4bOiJE5DlD/1A6P5OGOiZkLTop5ZI6HmKgOj/+tjoPIbc6WICeOslfETqluiM69lTZOZcSETrC\r\nW/U5nNerOl5/4TkurV868lPiOa74zzpIGNA6Hp7FOmDlVzo+G786lQ8zOl5vfzqb6AU7XHqwOhTK\r\nBzu90ZU6enIvOpUJkDqK5YY6h8BLOh5P1Dr+xAc69AZfOpHmLzp0Mq06B9WIOpOjwTkusG86CHRr\r\nOsCnijrIkwI619aGOh6ANjpstIg5aNJlOYRGKjpJfC46me9aOjIbBDrJNpI68Y0fOt6JtDlHWTU6\r\nEi+iOqCmkTrmkng6QKEWOmkHVzo+cYk62A5aOgKTWzpqzoo5+jgIOtguCzoGiQM6y6dYOqNczDmP\r\niZM5BJPQOJyNcTiGAg44RI7xN64YCjhwFQY4FcgMOPjJDDgbwAo41RYWOOKzBzhzVyY4KD4POJmV\r\nHDhi9Pc3WmwIODDWHTjizBw4QPn9N6+hDDj/dB04JZPmN2HR+DfVshE4sgL4NzwZCTh9du83Z28J\r\nOFv8Hzj2UQU42Ev+N84LIzpouRI6rTMAOMwTFjhShgk4b4X+N9G2GjiahAw4PS8EOM9N/jf9UA04\r\n5ej4N78XBzi24A448y8ROPvWDjhuxQQ4LNAfOM//Ezjl3u83emIjODPMBDiU4xI4G8QKOFknFDjo\r\nTQg4dpT6N7kxBThAFfs3O34EOLuHCzhWK/w3ZgUHOKiS9je7OAM4HM7rN01jFThtsuI32GcLOP+o\r\nAjgh+AM41IIKONiACDhPYQQ4FsAHOKrKEjh2efo3Me7/N+idBjhpyf830wD8N3CE8zf/aec3vPH+\r\nN3jfAThDJAI4JcXKNzL77jcXMBA4KPPVN1P1AziMrts3IaX5N9r8DjigZ+o3AEAEOA==", - "noiseTrainingType" : "default" - }, - "metadata" : { - "effectIndex" : "0" - } - } - ], - "start" : 0, - "duration" : 58918, - "mediaStart" : 18, - "mediaDuration" : 58918, - "scalar" : 1, - "animationTracks" : { - - } - } -, - "start" : 0, - "duration" : 58918, - "mediaStart" : 0, - "mediaDuration" : 58936, - "scalar" : 1, - "metadata" : { - "AutoAppliedSmartFocus" : "False", - "WinSubProjectDisplayName" : "", - "clipSpeedAttribute" : false - } - } - ] - }, - { - "trackIndex" : 1, - "medias" : [ - ] - } - ] - } - } - ] - }, - "trackAttributes" : [ - { - "ident" : "", - "audioMuted" : false, - "videoHidden" : false, - "metadata" : { - "IsLocked" : "False", - "WinTrackHeight" : "56" - } - }, - { - "ident" : "", - "audioMuted" : false, - "videoHidden" : false, - "metadata" : { - "IsLocked" : "False", - "WinTrackHeight" : "56" - } - } - ], - "captionAttributes" : { - "enabled" : true, - "fontName" : "Arial", - "fontSize" : 64, - "backgroundColor" : [ 0, 0, 0, 191], - "foregroundColor" : [ 255, 255, 255, 255], - "lang" : "en", - "alignment" : 0, - "defaultFontSize" : true, - "opacity" : 0.5, - "backgroundEnabled" : true, - "backgroundOnlyAroundText" : true - }, - "backgroundColor" : [ 0, 0, 0, 255] - }, - "metadata" : { - "AutoSaveFile" : "C:\\Users\\fbapt\\AppData\\Local\\TechSmith\\Camtasia Studio\\9.0\\Auto-Saves\\Untitled Project68af9d3b.autosave.tscproj", - "CanvasZoom" : 83, - "Date" : "2018-05-05 04:37:12 PM", - "IsAutoSave" : "0", - "Language" : "ENU", - "ProfileName" : "", - "ProjectDimensionsChanged" : "1", - "audioNarrationNotes" : "", - "calloutStyle" : "Basic", - "canvasDetached" : "False", - "canvasPositionHeight" : "0", - "canvasPositionLeft" : "0", - "canvasPositionTop" : "0", - "canvasPositionWidth" : "0" - } -} From 896b892bde7740f919d8e3f7763b59d80913f7c9 Mon Sep 17 00:00:00 2001 From: Fred Baptiste <6551123+fbaptiste@users.noreply.github.com> Date: Sun, 5 Mar 2023 21:34:26 -0700 Subject: [PATCH 16/27] Create GPL v3 license --- LICENSE | 674 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 674 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..f288702d --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. From 2c035c71ef743f42b132ed3b42de11f3ac71c015 Mon Sep 17 00:00:00 2001 From: Naeem <52814859+naeem-bebit@users.noreply.github.com> Date: Fri, 14 Apr 2023 09:04:32 +0800 Subject: [PATCH 17/27] fix typo --- .../03 - Closures.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Part 1/Section 07 - Scopes, Closures and Decorators/03 - Closures.ipynb b/Part 1/Section 07 - Scopes, Closures and Decorators/03 - Closures.ipynb index ce9fefa8..1ae2ffef 100644 --- a/Part 1/Section 07 - Scopes, Closures and Decorators/03 - Closures.ipynb +++ b/Part 1/Section 07 - Scopes, Closures and Decorators/03 - Closures.ipynb @@ -274,7 +274,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "As we saw in the lecture, we can set up nonlocal variables in different inner functionsd that reference the same outer scope variable, i.e. we have a free variable that is shared between two closures. This works because both non local variables and the outer local variable all point back to the same cell object." + "As we saw in the lecture, we can set up nonlocal variables in different inner functions that reference the same outer scope variable, i.e. we have a free variable that is shared between two closures. This works because both non local variables and the outer local variable all point back to the same cell object." ] }, { From d69207ffd3581ff4989c9da730f8561ceae96aea Mon Sep 17 00:00:00 2001 From: Viliam <84155138+vpopovec@users.noreply.github.com> Date: Thu, 27 Apr 2023 10:23:34 +0200 Subject: [PATCH 18/27] Update 09 - Decorator Application - Memoization.ipynb fix typo --- .../09 - Decorator Application - Memoization.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Part 1/Section 07 - Scopes, Closures and Decorators/09 - Decorator Application - Memoization.ipynb b/Part 1/Section 07 - Scopes, Closures and Decorators/09 - Decorator Application - Memoization.ipynb index 69225a59..a5d8c00e 100644 --- a/Part 1/Section 07 - Scopes, Closures and Decorators/09 - Decorator Application - Memoization.ipynb +++ b/Part 1/Section 07 - Scopes, Closures and Decorators/09 - Decorator Application - Memoization.ipynb @@ -1094,7 +1094,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "You'll not how Python had to recalculate `fib` for `10, 9,` etc. This is because the cache can only contain 10 items, so when we calculated `fib(20)`, it stored fib for `20, 19, ..., 11` (10 items) and therefore the oldest items fib `10, 9, ..., 1` were removed from the cache to make space." + "Note how Python had to recalculate `fib` for `10, 9,` etc. This is because the cache can only contain 10 items, so when we calculated `fib(20)`, it stored fib for `20, 19, ..., 11` (10 items) and therefore the oldest items fib `10, 9, ..., 1` were removed from the cache to make space." ] }, { From 503cce6ca376285e77249ddddd55c8491db17c59 Mon Sep 17 00:00:00 2001 From: fbaptiste <6551123+fbaptiste@users.noreply.github.com> Date: Sun, 30 Apr 2023 06:48:49 -0700 Subject: [PATCH 19/27] fix typo --- Part 4/Section 02 - Classes/02 - Class Attributes.ipynb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Part 4/Section 02 - Classes/02 - Class Attributes.ipynb b/Part 4/Section 02 - Classes/02 - Class Attributes.ipynb index 8e2d9852..94797e2d 100644 --- a/Part 4/Section 02 - Classes/02 - Class Attributes.ipynb +++ b/Part 4/Section 02 - Classes/02 - Class Attributes.ipynb @@ -581,13 +581,13 @@ } ], "source": [ - "__name__ in Program.__dict__" + "\"__name__\" in Program.__dict__" ] } ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -601,7 +601,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.7" + "version": "3.9.10" } }, "nbformat": 4, From 9b057cf5f50e7efeb333a6f7b48b70d85eb038a2 Mon Sep 17 00:00:00 2001 From: fbaptiste <6551123+fbaptiste@users.noreply.github.com> Date: Mon, 26 Jun 2023 08:06:01 -0700 Subject: [PATCH 20/27] typos --- .../04 - The While Loop.ipynb | 6 +- .../07 - Classes.ipynb | 220 +++++++----------- 2 files changed, 88 insertions(+), 138 deletions(-) diff --git a/Part 1/Section 02 - A Quick Refresher/04 - The While Loop.ipynb b/Part 1/Section 02 - A Quick Refresher/04 - The While Loop.ipynb index c68c2e56..13a189ef 100644 --- a/Part 1/Section 02 - A Quick Refresher/04 - The While Loop.ipynb +++ b/Part 1/Section 02 - A Quick Refresher/04 - The While Loop.ipynb @@ -11,7 +11,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The **while** loop is a way to repeatg a block of code as long as a specified condition is met." + "The **while** loop is a way to repeat a block of code as long as a specified condition is met." ] }, { @@ -366,7 +366,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -380,7 +380,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.1" + "version": "3.9.10" } }, "nbformat": 4, diff --git a/Part 1/Section 02 - A Quick Refresher/07 - Classes.ipynb b/Part 1/Section 02 - A Quick Refresher/07 - Classes.ipynb index af2865b3..6d9ae065 100644 --- a/Part 1/Section 02 - A Quick Refresher/07 - Classes.ipynb +++ b/Part 1/Section 02 - A Quick Refresher/07 - Classes.ipynb @@ -24,9 +24,7 @@ { "cell_type": "code", "execution_count": 1, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "class Rectangle:\n", @@ -41,7 +39,7 @@ "source": [ "We create **instances** of the `Rectangle` class by calling it with arguments that are passed to the `__init__` method as the second and third arguments. The first argument (`self`) is automatically filled in by Python and contains the object being created.\n", "\n", - "Note that using `self` is just a convention (although a good one, and you shgoudl use it to make your code more understandable by others), you could really call it whatever (valid) name you choose.\n", + "Note that using `self` is just a convention (although a good one, and you should use it to make your code more understandable by others), you could really call it whatever (valid) name you choose.\n", "\n", "But just because you can does not mean you should!" ] @@ -49,9 +47,7 @@ { "cell_type": "code", "execution_count": 2, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "r1 = Rectangle(10, 20)\n", @@ -126,9 +122,7 @@ { "cell_type": "code", "execution_count": 5, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "class Rectangle:\n", @@ -146,9 +140,7 @@ { "cell_type": "code", "execution_count": 6, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "r1 = Rectangle(10, 20)" @@ -218,9 +210,7 @@ { "cell_type": "code", "execution_count": 9, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "class Rectangle:\n", @@ -238,9 +228,7 @@ { "cell_type": "code", "execution_count": 10, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "r1 = Rectangle(10, 20)" @@ -299,7 +287,7 @@ { "data": { "text/plain": [ - "'<__main__.Rectangle object at 0x000002375E7006A0>'" + "'<__main__.Rectangle object at 0x103561af0>'" ] }, "execution_count": 12, @@ -323,9 +311,7 @@ { "cell_type": "code", "execution_count": 13, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "class Rectangle:\n", @@ -386,7 +372,7 @@ { "data": { "text/plain": [ - "'<__main__.Rectangle object at 0x000002375E708DA0>'" + "'<__main__.Rectangle object at 0x10356c5e0>'" ] }, "execution_count": 15, @@ -419,9 +405,7 @@ { "cell_type": "code", "execution_count": 16, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "class Rectangle:\n", @@ -442,9 +426,7 @@ { "cell_type": "code", "execution_count": 17, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "r1 = Rectangle(10, 20)" @@ -485,7 +467,7 @@ { "data": { "text/plain": [ - "<__main__.Rectangle at 0x2375e716ef0>" + "<__main__.Rectangle at 0x103570640>" ] }, "execution_count": 19, @@ -507,9 +489,7 @@ { "cell_type": "code", "execution_count": 20, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "class Rectangle:\n", @@ -533,9 +513,7 @@ { "cell_type": "code", "execution_count": 21, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "r1 = Rectangle(10, 20)" @@ -588,9 +566,7 @@ { "cell_type": "code", "execution_count": 24, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "r1 = Rectangle(10, 20)\n", @@ -633,10 +609,8 @@ }, { "cell_type": "code", - "execution_count": 40, - "metadata": { - "collapsed": true - }, + "execution_count": 26, + "metadata": {}, "outputs": [], "source": [ "class Rectangle:\n", @@ -666,10 +640,8 @@ }, { "cell_type": "code", - "execution_count": 41, - "metadata": { - "collapsed": true - }, + "execution_count": 27, + "metadata": {}, "outputs": [], "source": [ "r1 = Rectangle(10, 20)\n", @@ -678,7 +650,7 @@ }, { "cell_type": "code", - "execution_count": 42, + "execution_count": 28, "metadata": {}, "outputs": [ { @@ -687,7 +659,7 @@ "False" ] }, - "execution_count": 42, + "execution_count": 28, "metadata": {}, "output_type": "execute_result" } @@ -698,7 +670,7 @@ }, { "cell_type": "code", - "execution_count": 43, + "execution_count": 29, "metadata": {}, "outputs": [ { @@ -714,7 +686,7 @@ "True" ] }, - "execution_count": 43, + "execution_count": 29, "metadata": {}, "output_type": "execute_result" } @@ -725,10 +697,8 @@ }, { "cell_type": "code", - "execution_count": 44, - "metadata": { - "collapsed": true - }, + "execution_count": 30, + "metadata": {}, "outputs": [], "source": [ "r3 = Rectangle(2, 3)" @@ -736,7 +706,7 @@ }, { "cell_type": "code", - "execution_count": 45, + "execution_count": 31, "metadata": {}, "outputs": [ { @@ -752,7 +722,7 @@ "False" ] }, - "execution_count": 45, + "execution_count": 31, "metadata": {}, "output_type": "execute_result" } @@ -770,7 +740,7 @@ }, { "cell_type": "code", - "execution_count": 46, + "execution_count": 32, "metadata": {}, "outputs": [ { @@ -786,7 +756,7 @@ "False" ] }, - "execution_count": 46, + "execution_count": 32, "metadata": {}, "output_type": "execute_result" } @@ -804,10 +774,8 @@ }, { "cell_type": "code", - "execution_count": 47, - "metadata": { - "collapsed": true - }, + "execution_count": 33, + "metadata": {}, "outputs": [], "source": [ "class Rectangle:\n", @@ -847,10 +815,8 @@ }, { "cell_type": "code", - "execution_count": 48, - "metadata": { - "collapsed": true - }, + "execution_count": 34, + "metadata": {}, "outputs": [], "source": [ "class Rectangle:\n", @@ -885,10 +851,8 @@ }, { "cell_type": "code", - "execution_count": 49, - "metadata": { - "collapsed": true - }, + "execution_count": 35, + "metadata": {}, "outputs": [], "source": [ "r1 = Rectangle(100, 200)\n", @@ -897,7 +861,7 @@ }, { "cell_type": "code", - "execution_count": 50, + "execution_count": 36, "metadata": {}, "outputs": [ { @@ -906,7 +870,7 @@ "False" ] }, - "execution_count": 50, + "execution_count": 36, "metadata": {}, "output_type": "execute_result" } @@ -917,7 +881,7 @@ }, { "cell_type": "code", - "execution_count": 51, + "execution_count": 37, "metadata": {}, "outputs": [ { @@ -926,7 +890,7 @@ "True" ] }, - "execution_count": 51, + "execution_count": 37, "metadata": {}, "output_type": "execute_result" } @@ -944,7 +908,7 @@ }, { "cell_type": "code", - "execution_count": 52, + "execution_count": 38, "metadata": {}, "outputs": [ { @@ -953,7 +917,7 @@ "True" ] }, - "execution_count": 52, + "execution_count": 38, "metadata": {}, "output_type": "execute_result" } @@ -984,7 +948,7 @@ }, { "cell_type": "code", - "execution_count": 53, + "execution_count": 39, "metadata": {}, "outputs": [ { @@ -992,10 +956,10 @@ "evalue": "'<=' not supported between instances of 'Rectangle' and 'Rectangle'", "output_type": "error", "traceback": [ - "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[1;31mTypeError\u001b[0m Traceback (most recent call last)", - "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m()\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mr1\u001b[0m \u001b[1;33m<=\u001b[0m \u001b[0mr2\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[1;31mTypeError\u001b[0m: '<=' not supported between instances of 'Rectangle' and 'Rectangle'" + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "Input \u001b[0;32mIn [39]\u001b[0m, in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mr1\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m<\u001b[39;49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m \u001b[49m\u001b[43mr2\u001b[49m\n", + "\u001b[0;31mTypeError\u001b[0m: '<=' not supported between instances of 'Rectangle' and 'Rectangle'" ] } ], @@ -1028,10 +992,8 @@ }, { "cell_type": "code", - "execution_count": 54, - "metadata": { - "collapsed": true - }, + "execution_count": 40, + "metadata": {}, "outputs": [], "source": [ "class Rectangle:\n", @@ -1045,10 +1007,8 @@ }, { "cell_type": "code", - "execution_count": 55, - "metadata": { - "collapsed": true - }, + "execution_count": 41, + "metadata": {}, "outputs": [], "source": [ "r1 = Rectangle(10, 20)" @@ -1056,7 +1016,7 @@ }, { "cell_type": "code", - "execution_count": 56, + "execution_count": 42, "metadata": {}, "outputs": [ { @@ -1065,7 +1025,7 @@ "10" ] }, - "execution_count": 56, + "execution_count": 42, "metadata": {}, "output_type": "execute_result" } @@ -1076,10 +1036,8 @@ }, { "cell_type": "code", - "execution_count": 57, - "metadata": { - "collapsed": true - }, + "execution_count": 43, + "metadata": {}, "outputs": [], "source": [ "r1.width = 100" @@ -1087,7 +1045,7 @@ }, { "cell_type": "code", - "execution_count": 58, + "execution_count": 44, "metadata": {}, "outputs": [ { @@ -1096,7 +1054,7 @@ "Rectangle(100, 20)" ] }, - "execution_count": 58, + "execution_count": 44, "metadata": {}, "output_type": "execute_result" } @@ -1130,10 +1088,8 @@ }, { "cell_type": "code", - "execution_count": 59, - "metadata": { - "collapsed": true - }, + "execution_count": 45, + "metadata": {}, "outputs": [], "source": [ "class Rectangle:\n", @@ -1167,10 +1123,8 @@ }, { "cell_type": "code", - "execution_count": 60, - "metadata": { - "collapsed": true - }, + "execution_count": 46, + "metadata": {}, "outputs": [], "source": [ "r1 = Rectangle(10, 20)" @@ -1178,7 +1132,7 @@ }, { "cell_type": "code", - "execution_count": 61, + "execution_count": 47, "metadata": {}, "outputs": [ { @@ -1187,7 +1141,7 @@ "10" ] }, - "execution_count": 61, + "execution_count": 47, "metadata": {}, "output_type": "execute_result" } @@ -1198,10 +1152,8 @@ }, { "cell_type": "code", - "execution_count": 62, - "metadata": { - "collapsed": true - }, + "execution_count": 48, + "metadata": {}, "outputs": [], "source": [ "r1.width = 100" @@ -1209,7 +1161,7 @@ }, { "cell_type": "code", - "execution_count": 63, + "execution_count": 49, "metadata": {}, "outputs": [ { @@ -1218,7 +1170,7 @@ "Rectangle(100, 20)" ] }, - "execution_count": 63, + "execution_count": 49, "metadata": {}, "output_type": "execute_result" } @@ -1229,7 +1181,7 @@ }, { "cell_type": "code", - "execution_count": 64, + "execution_count": 50, "metadata": {}, "outputs": [ { @@ -1237,11 +1189,11 @@ "evalue": "Width must be positive.", "output_type": "error", "traceback": [ - "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[1;31mValueError\u001b[0m Traceback (most recent call last)", - "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m()\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mr1\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mwidth\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;33m-\u001b[0m\u001b[1;36m10\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[1;32m\u001b[0m in \u001b[0;36mwidth\u001b[1;34m(self, width)\u001b[0m\n\u001b[0;32m 14\u001b[0m \u001b[1;32mdef\u001b[0m \u001b[0mwidth\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mwidth\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 15\u001b[0m \u001b[1;32mif\u001b[0m \u001b[0mwidth\u001b[0m \u001b[1;33m<=\u001b[0m \u001b[1;36m0\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 16\u001b[1;33m \u001b[1;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'Width must be positive.'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 17\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_width\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mwidth\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 18\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n", - "\u001b[1;31mValueError\u001b[0m: Width must be positive." + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", + "Input \u001b[0;32mIn [50]\u001b[0m, in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0m r1\u001b[38;5;241m.\u001b[39mwidth \u001b[38;5;241m=\u001b[39m \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m10\u001b[39m\n", + "Input \u001b[0;32mIn [45]\u001b[0m, in \u001b[0;36mRectangle.width\u001b[0;34m(self, width)\u001b[0m\n\u001b[1;32m 13\u001b[0m \u001b[38;5;129m@width\u001b[39m\u001b[38;5;241m.\u001b[39msetter\n\u001b[1;32m 14\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mwidth\u001b[39m(\u001b[38;5;28mself\u001b[39m, width):\n\u001b[1;32m 15\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m width \u001b[38;5;241m<\u001b[39m\u001b[38;5;241m=\u001b[39m \u001b[38;5;241m0\u001b[39m:\n\u001b[0;32m---> 16\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;124mWidth must be positive.\u001b[39m\u001b[38;5;124m'\u001b[39m)\n\u001b[1;32m 17\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_width \u001b[38;5;241m=\u001b[39m width\n", + "\u001b[0;31mValueError\u001b[0m: Width must be positive." ] } ], @@ -1258,10 +1210,8 @@ }, { "cell_type": "code", - "execution_count": 65, - "metadata": { - "collapsed": true - }, + "execution_count": 51, + "metadata": {}, "outputs": [], "source": [ "class Rectangle:\n", @@ -1298,7 +1248,7 @@ }, { "cell_type": "code", - "execution_count": 66, + "execution_count": 52, "metadata": {}, "outputs": [ { @@ -1306,12 +1256,12 @@ "evalue": "Width must be positive.", "output_type": "error", "traceback": [ - "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[1;31mValueError\u001b[0m Traceback (most recent call last)", - "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m()\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mr1\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mRectangle\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;36m10\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[1;32m\u001b[0m in \u001b[0;36m__init__\u001b[1;34m(self, width, height)\u001b[0m\n\u001b[0;32m 4\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_height\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;32mNone\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 5\u001b[0m \u001b[1;31m# now we call our accessor methods to set the width and height\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 6\u001b[1;33m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mwidth\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mwidth\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 7\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mheight\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mheight\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 8\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n", - "\u001b[1;32m\u001b[0m in \u001b[0;36mwidth\u001b[1;34m(self, width)\u001b[0m\n\u001b[0;32m 17\u001b[0m \u001b[1;32mdef\u001b[0m \u001b[0mwidth\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mwidth\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 18\u001b[0m \u001b[1;32mif\u001b[0m \u001b[0mwidth\u001b[0m \u001b[1;33m<=\u001b[0m \u001b[1;36m0\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 19\u001b[1;33m \u001b[1;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'Width must be positive.'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 20\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_width\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mwidth\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 21\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n", - "\u001b[1;31mValueError\u001b[0m: Width must be positive." + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", + "Input \u001b[0;32mIn [52]\u001b[0m, in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0m r1 \u001b[38;5;241m=\u001b[39m \u001b[43mRectangle\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;43m10\u001b[39;49m\u001b[43m)\u001b[49m\n", + "Input \u001b[0;32mIn [51]\u001b[0m, in \u001b[0;36mRectangle.__init__\u001b[0;34m(self, width, height)\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_height \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[1;32m 5\u001b[0m \u001b[38;5;66;03m# now we call our accessor methods to set the width and height\u001b[39;00m\n\u001b[0;32m----> 6\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mwidth \u001b[38;5;241m=\u001b[39m width\n\u001b[1;32m 7\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mheight \u001b[38;5;241m=\u001b[39m height\n", + "Input \u001b[0;32mIn [51]\u001b[0m, in \u001b[0;36mRectangle.width\u001b[0;34m(self, width)\u001b[0m\n\u001b[1;32m 16\u001b[0m \u001b[38;5;129m@width\u001b[39m\u001b[38;5;241m.\u001b[39msetter\n\u001b[1;32m 17\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mwidth\u001b[39m(\u001b[38;5;28mself\u001b[39m, width):\n\u001b[1;32m 18\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m width \u001b[38;5;241m<\u001b[39m\u001b[38;5;241m=\u001b[39m \u001b[38;5;241m0\u001b[39m:\n\u001b[0;32m---> 19\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;124mWidth must be positive.\u001b[39m\u001b[38;5;124m'\u001b[39m)\n\u001b[1;32m 20\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_width \u001b[38;5;241m=\u001b[39m width\n", + "\u001b[0;31mValueError\u001b[0m: Width must be positive." ] } ], @@ -1323,7 +1273,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "There more we should be doing, like checking that the width and height being passed in are numeric types, and so on. Especially during the `__init__` phase - we would rather raise an exception when the object is being created rather than delay things and raise an exception when the user calls some method like `area` - that way the exception will be on the line that creates the object - makes debugging much easier!" + "There is more we should be doing, like checking that the width and height being passed in are numeric types, and so on. Especially during the `__init__` phase - we would rather raise an exception when the object is being created rather than delay things and raise an exception when the user calls some method like `area` - that way the exception will be on the line that creates the object - makes debugging much easier!" ] }, { @@ -1336,7 +1286,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -1350,7 +1300,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.2" + "version": "3.9.10" } }, "nbformat": 4, From 92546329a21f6818004971e0251400e8ced71ea3 Mon Sep 17 00:00:00 2001 From: Cameron Beck <94604154+cbkinase@users.noreply.github.com> Date: Mon, 9 Oct 2023 03:34:56 -0400 Subject: [PATCH 21/27] Update Project 5 - Description.ipynb --- .../01 - Description/Project 5 - Description.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Part 2/Section 11 - Project 5/01 - Description/Project 5 - Description.ipynb b/Part 2/Section 11 - Project 5/01 - Description/Project 5 - Description.ipynb index 680d9825..81b675f1 100644 --- a/Part 2/Section 11 - Project 5/01 - Description/Project 5 - Description.ipynb +++ b/Part 2/Section 11 - Project 5/01 - Description/Project 5 - Description.ipynb @@ -169,7 +169,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The dialect of a CSV file refers to some of the specifics used to define data in a CSV file. The separators can be different (for example some failes use a comma, some use a semi-colon, some use a tab, etc).\n", + "The dialect of a CSV file refers to some of the specifics used to define data in a CSV file. The separators can be different (for example some files use a comma, some use a semi-colon, some use a tab, etc).\n", "\n", "Also, as we have seen before, a field is also sometimes delimited using quotes, or double quotes, or maybe some entirely different character.\n", "\n", From 2c3a63f7a5ca2e3fabf33bb88cb92fdff76c145a Mon Sep 17 00:00:00 2001 From: Cameron Beck <94604154+cbkinase@users.noreply.github.com> Date: Tue, 10 Oct 2023 01:13:19 -0400 Subject: [PATCH 22/27] Update Project 5 - Description.ipynb --- .../01 - Description/Project 5 - Description.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Part 2/Section 11 - Project 5/01 - Description/Project 5 - Description.ipynb b/Part 2/Section 11 - Project 5/01 - Description/Project 5 - Description.ipynb index 81b675f1..27a4e1d6 100644 --- a/Part 2/Section 11 - Project 5/01 - Description/Project 5 - Description.ipynb +++ b/Part 2/Section 11 - Project 5/01 - Description/Project 5 - Description.ipynb @@ -173,7 +173,7 @@ "\n", "Also, as we have seen before, a field is also sometimes delimited using quotes, or double quotes, or maybe some entirely different character.\n", "\n", - "When we have to deal with files that may be encoded using different dialects it can require quite a bit of work to determine what those specifics are. This is were the `Sniffer` class from the `csv` module can be useful. By providing it a sample fo the CSV file, it can analyze it and determine a best guess as to the specific dialect that was used. We can then use that dialect when we use the `csv.reader` function.\n", + "When we have to deal with files that may be encoded using different dialects it can require quite a bit of work to determine what those specifics are. This is where the `Sniffer` class from the `csv` module can be useful. By providing it a sample fo the CSV file, it can analyze it and determine a best guess as to the specific dialect that was used. We can then use that dialect when we use the `csv.reader` function.\n", "\n", "Let's see how to use it with one of our files: `personal_info.csv`:" ] From e9bcdea1901cfb3bddd276c916c3eef77d7e4c0e Mon Sep 17 00:00:00 2001 From: Cameron Beck <94604154+cbkinase@users.noreply.github.com> Date: Tue, 10 Oct 2023 01:15:07 -0400 Subject: [PATCH 23/27] Update Project 5 - Description.ipynb --- .../01 - Description/Project 5 - Description.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Part 2/Section 11 - Project 5/01 - Description/Project 5 - Description.ipynb b/Part 2/Section 11 - Project 5/01 - Description/Project 5 - Description.ipynb index 27a4e1d6..f89d4ca4 100644 --- a/Part 2/Section 11 - Project 5/01 - Description/Project 5 - Description.ipynb +++ b/Part 2/Section 11 - Project 5/01 - Description/Project 5 - Description.ipynb @@ -173,7 +173,7 @@ "\n", "Also, as we have seen before, a field is also sometimes delimited using quotes, or double quotes, or maybe some entirely different character.\n", "\n", - "When we have to deal with files that may be encoded using different dialects it can require quite a bit of work to determine what those specifics are. This is where the `Sniffer` class from the `csv` module can be useful. By providing it a sample fo the CSV file, it can analyze it and determine a best guess as to the specific dialect that was used. We can then use that dialect when we use the `csv.reader` function.\n", + "When we have to deal with files that may be encoded using different dialects it can require quite a bit of work to determine what those specifics are. This is where the `Sniffer` class from the `csv` module can be useful. By providing it a sample of the CSV file, it can analyze it and determine a best guess as to the specific dialect that was used. We can then use that dialect when we use the `csv.reader` function.\n", "\n", "Let's see how to use it with one of our files: `personal_info.csv`:" ] From fe407ef96e4c94627a3b1471b4a73688a043f666 Mon Sep 17 00:00:00 2001 From: fbaptiste <6551123+fbaptiste@users.noreply.github.com> Date: Fri, 8 Dec 2023 21:18:33 -0700 Subject: [PATCH 24/27] fix data --- Part 2/Section 07 - Project 3/nyc_parking_tickets_extract.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Part 2/Section 07 - Project 3/nyc_parking_tickets_extract.csv b/Part 2/Section 07 - Project 3/nyc_parking_tickets_extract.csv index 589d9c27..85f55732 100644 --- a/Part 2/Section 07 - Project 3/nyc_parking_tickets_extract.csv +++ b/Part 2/Section 07 - Project 3/nyc_parking_tickets_extract.csv @@ -888,7 +888,7 @@ Summons Number,Plate ID,Registration State,Plate Type,Issue Date,Violation Code, 7007488663,VFP7291,VA,PAS,3/2/2017,53,4DSD,ME/BE,53-Safety Zone 1402409825,869ZR9,MA,999,10/15/2016,53,SUBN,SATUR, 8506603031,2383103,IN,PAS,2/1/2017,53,TRAC,VOLVO,53-Safety Zone -8480309064,51771JW,NY,COM,1/26/2017,64,VAN,INTER,"64-No STD Ex Con/DPL, D/S Dec" +8480309064,51771JW,NY,COM,1/26/2017,64,VAN,INTER,64-No STD Ex Con/DPL 8162054819,EW866X,NJ,PAS,1/29/2017,68,4DSD,NISSA,68-Not Pkg. Comp. w Psted Sign 8343032536,4CMH482,CA,PAS,10/3/2016,68,SUBN,SUBAR,68-Not Pkg. Comp. w Psted Sign 8084034959,ASW2109,NY,PAS,12/20/2016,68,4DSD,TOYOT,68-Not Pkg. Comp. w Psted Sign From 0831e0eeaf93227dd9f124fc977cb1842692982a Mon Sep 17 00:00:00 2001 From: fbaptiste <6551123+fbaptiste@users.noreply.github.com> Date: Tue, 12 Dec 2023 21:21:29 -0700 Subject: [PATCH 25/27] clarified description --- .../project_4_description/Project - Description.ipynb | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Part 2/Section 09 - Project 4/project_4_description/Project - Description.ipynb b/Part 2/Section 09 - Project 4/project_4_description/Project - Description.ipynb index 1f8e4da0..506d2e9a 100644 --- a/Part 2/Section 09 - Project 4/project_4_description/Project - Description.ipynb +++ b/Part 2/Section 09 - Project 4/project_4_description/Project - Description.ipynb @@ -48,11 +48,10 @@ "Create a single iterable that combines all the columns from all the iterators.\n", "\n", "The iterable should yield named tuples containing all the columns.\n", - "Make sure that the SSN's across the files match!\n", "\n", - "All the files are guaranteed to be in SSN sort order, and every SSN is unique, and every SSN appears in every file.\n", + "Normally you should ensure that the SSN's match across all the files (i.e. identical row numbers in each file refer to the same SSN). However, in this case you do not need to do so as the data is already \"aligned\" - all the files are guaranteed to be in SSN sort order, every SSN is unique, and every SSN appears in every file.\n", "\n", - "Make sure the SSN is not repeated 4 times - one time per row is enough!" + "Just make sure that in your iterable the SSN is not repeated 4 times - one time per row is enough!" ] }, { @@ -154,7 +153,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -168,7 +167,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.2" + "version": "3.9.10" } }, "nbformat": 4, From 24dd6388b3db8f8ca3061b1ad796798de370020b Mon Sep 17 00:00:00 2001 From: Irvanal Haq Date: Fri, 2 Aug 2024 16:19:49 +0700 Subject: [PATCH 26/27] add return fn in inner function of decorator counter --- .../06 - Decorators - Part 1.ipynb | 1 + 1 file changed, 1 insertion(+) diff --git a/Part 1/Section 07 - Scopes, Closures and Decorators/06 - Decorators - Part 1.ipynb b/Part 1/Section 07 - Scopes, Closures and Decorators/06 - Decorators - Part 1.ipynb index 919c8673..1f998866 100644 --- a/Part 1/Section 07 - Scopes, Closures and Decorators/06 - Decorators - Part 1.ipynb +++ b/Part 1/Section 07 - Scopes, Closures and Decorators/06 - Decorators - Part 1.ipynb @@ -645,6 +645,7 @@ " nonlocal count\n", " count += 1\n", " print(\"{0} was called {1} times\".format(fn.__name__, count))\n", + " return fn(*args, **kwargs)\n", "\n", " return inner" ] From 1bc227b4d790ebf2ee2e6b4e76d05111bfe471ad Mon Sep 17 00:00:00 2001 From: Irvanal Haq Date: Fri, 2 Aug 2024 22:52:51 +0700 Subject: [PATCH 27/27] fix typo anther to another --- .../02 - Nonlocal Scopes.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Part 1/Section 07 - Scopes, Closures and Decorators/02 - Nonlocal Scopes.ipynb b/Part 1/Section 07 - Scopes, Closures and Decorators/02 - Nonlocal Scopes.ipynb index e250d7fb..84ae579e 100644 --- a/Part 1/Section 07 - Scopes, Closures and Decorators/02 - Nonlocal Scopes.ipynb +++ b/Part 1/Section 07 - Scopes, Closures and Decorators/02 - Nonlocal Scopes.ipynb @@ -11,7 +11,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Functions defined inside anther function can reference variables from that enclosing scope, just like functions can reference variables from the global scope." + "Functions defined inside another function can reference variables from that enclosing scope, just like functions can reference variables from the global scope." ] }, {