diff options
| author | Simeon Simeonov | 2026-03-29 13:43:38 +0200 |
|---|---|---|
| committer | Simeon Simeonov | 2026-03-29 13:43:38 +0200 |
| commit | be44243136d710eec0345f8459a64da377bab357 (patch) | |
| tree | 2abf9cdb409bd8a267caad42e4ff683b0e068c5a /notebooks | |
| parent | 63acc390e6417a716ebd789c07530414db7d0981 (diff) | |
Diffstat (limited to 'notebooks')
| -rw-r--r-- | notebooks/python/cryptographic_primitives.ipynb | 261 | ||||
| -rw-r--r-- | notebooks/python/python_3_8_to_3_11.ipynb | 2 | ||||
| -rw-r--r-- | notebooks/python/python_intro.ipynb | 120 | ||||
| -rw-r--r-- | notebooks/python/python_oo.ipynb | 151 |
4 files changed, 451 insertions, 83 deletions
diff --git a/notebooks/python/cryptographic_primitives.ipynb b/notebooks/python/cryptographic_primitives.ipynb new file mode 100644 index 0000000..4d96549 --- /dev/null +++ b/notebooks/python/cryptographic_primitives.ipynb | |||
| @@ -0,0 +1,261 @@ | |||
| 1 | { | ||
| 2 | "cells": [ | ||
| 3 | { | ||
| 4 | "cell_type": "code", | ||
| 5 | "execution_count": 12, | ||
| 6 | "id": "fedd7d1a-36a9-4255-8305-a720dfb7b500", | ||
| 7 | "metadata": {}, | ||
| 8 | "outputs": [ | ||
| 9 | { | ||
| 10 | "name": "stdout", | ||
| 11 | "output_type": "stream", | ||
| 12 | "text": [ | ||
| 13 | "b\"{\\x8ea\\xf8wu!\\xeb\\xc4\\xc9\\xcb|\\\\\\xf1K\\xd5t\\x80i\\xbcD\\xb6!\\xfc\\xee\\x06lQ'V0\\xc6\"\n", | ||
| 14 | "7b8e61f8777521ebc4c9cb7c5cf14bd5748069bc44b621fcee066c51275630c6\n", | ||
| 15 | "b'e45h+Hd1IevEyct8XPFL1XSAabxEtiH87gZsUSdWMMY='\n" | ||
| 16 | ] | ||
| 17 | } | ||
| 18 | ], | ||
| 19 | "source": [ | ||
| 20 | "# hashes\n", | ||
| 21 | "\n", | ||
| 22 | "import base64\n", | ||
| 23 | "import hashlib\n", | ||
| 24 | "\n", | ||
| 25 | "hashed_result = hashlib.sha256(b\"This is a tesu\")\n", | ||
| 26 | "print(hashed_result.digest())\n", | ||
| 27 | "print(hashed_result.hexdigest())\n", | ||
| 28 | "print(base64.b64encode(hashed_result.digest()))\n" | ||
| 29 | ] | ||
| 30 | }, | ||
| 31 | { | ||
| 32 | "cell_type": "code", | ||
| 33 | "execution_count": 13, | ||
| 34 | "id": "9082c388-1c30-46dc-8897-15bf5db255c8", | ||
| 35 | "metadata": {}, | ||
| 36 | "outputs": [ | ||
| 37 | { | ||
| 38 | "name": "stdout", | ||
| 39 | "output_type": "stream", | ||
| 40 | "text": [ | ||
| 41 | "password_hash1 = 'pbkdf2_sha256$100000$j2jYEb1TvX/wh5JEAYGag6h+hRNTGe3rjyv5pegVBu4=$7cV7MljnJJYuGndge3jozwFP28aP8Gfj+QFGvv5bpfo='\n", | ||
| 42 | "password_hash2 = 'pbkdf2_sha256$100000$8+PsThJmJXNAbQ/zaR5AgRA/CuNv8L1uPUXRNuWHc9k=$Kf07CSCVQOTQrvWX5l/ZBdA7zsaEFH247QjUaewpxaU='\n" | ||
| 43 | ] | ||
| 44 | } | ||
| 45 | ], | ||
| 46 | "source": [ | ||
| 47 | "# PBKDF2\n", | ||
| 48 | "\n", | ||
| 49 | "import base64\n", | ||
| 50 | "import hashlib\n", | ||
| 51 | "import os\n", | ||
| 52 | "\n", | ||
| 53 | "\n", | ||
| 54 | "def get_password_hash(plaintext_password: str) -> str:\n", | ||
| 55 | " \"\"\"\n", | ||
| 56 | " Returns a complete password hash based on `plaintext_password`\n", | ||
| 57 | "\n", | ||
| 58 | " :return: The hashed version of `plaintext_password`\n", | ||
| 59 | " :rtype: str\n", | ||
| 60 | " \"\"\"\n", | ||
| 61 | " # parameters typycally sent as arguments or fetched from config\n", | ||
| 62 | " hash_algo = \"sha256\" # the hash algorithm to use for HMAC\n", | ||
| 63 | " iterations = 100000 # amount of iterations\n", | ||
| 64 | "\n", | ||
| 65 | " salt = os.urandom(32) # 32 bytes of random data\n", | ||
| 66 | "\n", | ||
| 67 | " key = hashlib.pbkdf2_hmac(hash_algo, plaintext_password.encode(), salt, iterations)\n", | ||
| 68 | "\n", | ||
| 69 | " return (\n", | ||
| 70 | " f\"pbkdf2_{hash_algo}${iterations}$\"\n", | ||
| 71 | " f\"{base64.b64encode(salt).decode(\"ascii\")}$\"\n", | ||
| 72 | " f\"{base64.b64encode(key).decode(\"ascii\")}\"\n", | ||
| 73 | " )\n", | ||
| 74 | "\n", | ||
| 75 | "\n", | ||
| 76 | "def password_matches(plaintext_password: str, password_hash: str) -> bool:\n", | ||
| 77 | " \"\"\"Returns True if `plaintext_password` matches `password_hash`, False otherwise\"\"\"\n", | ||
| 78 | " hash_tokens = password_hash.split(\"$\")\n", | ||
| 79 | " hash_algo = hash_tokens[0].split(\"_\")[1]\n", | ||
| 80 | " iterations = int(hash_tokens[1])\n", | ||
| 81 | " salt = base64.b64decode(hash_tokens[2])\n", | ||
| 82 | " key = hashlib.pbkdf2_hmac(hash_algo, plaintext_password.encode(), salt, iterations)\n", | ||
| 83 | " return key == base64.b64decode(hash_tokens[3])\n", | ||
| 84 | "\n", | ||
| 85 | "\n", | ||
| 86 | "password_hash1 = get_password_hash(\"didn't expect a Spanish Inquisition!\")\n", | ||
| 87 | "password_hash2 = get_password_hash(\"didn't expect a Spanish Inquisition!\")\n", | ||
| 88 | "print(f\"{password_hash1 = }\")\n", | ||
| 89 | "print(f\"{password_hash2 = }\")\n", | ||
| 90 | "\n", | ||
| 91 | "# print(password_matches(\"didn't expect a Spanish Inquisition!\", 'pbkdf2_sha256$100000$JfwrS72mFimypbwhJ/NCyds2dkXtknvfWM5AM3+Z5GQ=$k8YA8csSJZhuYW0um7utq3+lSX5pTAQPd9dukObWrPo='))" | ||
| 92 | ] | ||
| 93 | }, | ||
| 94 | { | ||
| 95 | "cell_type": "code", | ||
| 96 | "execution_count": 14, | ||
| 97 | "id": "1274325e-f670-4af8-a0bc-2ff7f668f769", | ||
| 98 | "metadata": {}, | ||
| 99 | "outputs": [ | ||
| 100 | { | ||
| 101 | "name": "stdout", | ||
| 102 | "output_type": "stream", | ||
| 103 | "text": [ | ||
| 104 | "('gxk9W0XFgbzbjRWB7zCPjbWs3', '00000bf5db2c3c6484904bf5e8314aeb123f5594af1f283fb62e14c7d196e84c')\n" | ||
| 105 | ] | ||
| 106 | } | ||
| 107 | ], | ||
| 108 | "source": [ | ||
| 109 | "# Proof of work\n", | ||
| 110 | "\n", | ||
| 111 | "import hashlib\n", | ||
| 112 | "import random\n", | ||
| 113 | "import string\n", | ||
| 114 | "\n", | ||
| 115 | "VALID_CHARS = string.ascii_letters + string.digits # valid characters for response\n", | ||
| 116 | "\n", | ||
| 117 | "\n", | ||
| 118 | "def pow_solve_rnd(challenge: str, starting_chars: str = \"00000\", answer_size: int = 25) -> tuple:\n", | ||
| 119 | " \"\"\"\n", | ||
| 120 | " Solves a POW challenge for SHA-256 using `random.choice`\n", | ||
| 121 | "\n", | ||
| 122 | " :return: (response, SHA-256 digest) tuple that solves the challenge\n", | ||
| 123 | " :rtype: tuple\n", | ||
| 124 | " \"\"\"\n", | ||
| 125 | " while True:\n", | ||
| 126 | " attempt = ''.join(\n", | ||
| 127 | " [random.choice(VALID_CHARS) for x in range(answer_size)])\n", | ||
| 128 | " dig = hashlib.sha256(('{0}:{1}'.format(challenge, attempt)).encode()).hexdigest()\n", | ||
| 129 | " if dig.startswith(starting_chars):\n", | ||
| 130 | " return attempt, dig\n", | ||
| 131 | "\n", | ||
| 132 | "print(f\"{pow_solve_rnd('2024-08-07')}\")" | ||
| 133 | ] | ||
| 134 | }, | ||
| 135 | { | ||
| 136 | "cell_type": "code", | ||
| 137 | "execution_count": 25, | ||
| 138 | "id": "6752c055-7e49-46c4-858b-599d069a7e77", | ||
| 139 | "metadata": {}, | ||
| 140 | "outputs": [ | ||
| 141 | { | ||
| 142 | "name": "stdout", | ||
| 143 | "output_type": "stream", | ||
| 144 | "text": [ | ||
| 145 | "enc-val$2$6ppVDmiJi0P9BnHuzwFUPA+vV/Gsb4INe2O9E/Ma2uI=$6879D8rw8645nbQ/NQtY3Q+AwKTWpd1P6Q==\n", | ||
| 146 | "Invalid tag when decrypting enc-val$2$tmuH3MTDQ9vMbxJ1pZUb0rQlF43Lgr/AYfkA+vSOZs0=$5CVt3VrxhuuOOVt1vCXeGpbhNh/20IcFNg==\n", | ||
| 147 | "None\n" | ||
| 148 | ] | ||
| 149 | } | ||
| 150 | ], | ||
| 151 | "source": [ | ||
| 152 | "# Encryption / decryption using AES-256-GCM\n", | ||
| 153 | "\n", | ||
| 154 | "import hashlib\n", | ||
| 155 | "import os\n", | ||
| 156 | "\n", | ||
| 157 | "from cryptography.exceptions import InvalidTag\n", | ||
| 158 | "from cryptography.hazmat.primitives.ciphers.aead import AESGCM\n", | ||
| 159 | "\n", | ||
| 160 | "\n", | ||
| 161 | "def encrypt(password: str, data: str) -> str:\n", | ||
| 162 | " \"\"\"\n", | ||
| 163 | " Encrypts `data` using `password` and AES-256-GCM.\n", | ||
| 164 | "\n", | ||
| 165 | " The output string is in the following format:\n", | ||
| 166 | " enc-val$`version-num`$`bas64-salt`$`base64-encrypted_data`\n", | ||
| 167 | "\n", | ||
| 168 | " :param password: The password to generate the key with\n", | ||
| 169 | " :type password: str\n", | ||
| 170 | "\n", | ||
| 171 | " :param data: The data to be encrypted\n", | ||
| 172 | " :type data: str\n", | ||
| 173 | "\n", | ||
| 174 | " :return: The output string\n", | ||
| 175 | " :rtype: str\n", | ||
| 176 | " \"\"\"\n", | ||
| 177 | " data_bytes = data.encode()\n", | ||
| 178 | " salt = os.urandom(32)\n", | ||
| 179 | " aesgcm = AESGCM(\n", | ||
| 180 | " hashlib.scrypt(\n", | ||
| 181 | " password.encode(), salt=salt, n=2**14, r=8, p=1, dklen=32\n", | ||
| 182 | " )\n", | ||
| 183 | " )\n", | ||
| 184 | " nonce = salt[:12]\n", | ||
| 185 | " # padding_length_bytes = b\"-1\" # no padding used 2 bytes \"sign\"\n", | ||
| 186 | " edata = aesgcm.encrypt(\n", | ||
| 187 | " nonce, data_bytes, salt\n", | ||
| 188 | " )\n", | ||
| 189 | " return (\n", | ||
| 190 | " f\"enc-val$2${base64.b64encode(salt).decode()}${base64.b64encode(edata).decode()}\"\n", | ||
| 191 | " )\n", | ||
| 192 | "\n", | ||
| 193 | "\n", | ||
| 194 | "def decrypt(password: str, edata: str) -> str:\n", | ||
| 195 | " \"\"\"\n", | ||
| 196 | " Decrypts `edata` using `password`.\n", | ||
| 197 | "\n", | ||
| 198 | " `edata` is in the following format:\n", | ||
| 199 | " enc-val$`version-num`$`bas64-salt`$`base64-encrypted_data`\n", | ||
| 200 | "\n", | ||
| 201 | " :param password: The password to generate the key with\n", | ||
| 202 | " :type password: str\n", | ||
| 203 | "\n", | ||
| 204 | " :param edata: The data to be decrypted\n", | ||
| 205 | " :type edata: str\n", | ||
| 206 | "\n", | ||
| 207 | " :return: The output string / decrypted data\n", | ||
| 208 | " :rtype: str\n", | ||
| 209 | " \"\"\"\n", | ||
| 210 | " # check for supported versions first...\n", | ||
| 211 | "\n", | ||
| 212 | " try:\n", | ||
| 213 | " salt, data = (base64.b64decode(t) for t in edata[10:].split('$'))\n", | ||
| 214 | " nonce = salt[:12]\n", | ||
| 215 | " aesgcm = AESGCM(\n", | ||
| 216 | " hashlib.scrypt(\n", | ||
| 217 | " password.encode(), salt=salt, n=2**14, r=8, p=1, dklen=32\n", | ||
| 218 | " )\n", | ||
| 219 | " )\n", | ||
| 220 | "\n", | ||
| 221 | " # decrypt\n", | ||
| 222 | " data = aesgcm.decrypt(nonce, data, salt)\n", | ||
| 223 | " return data.decode()\n", | ||
| 224 | " except InvalidTag:\n", | ||
| 225 | " print(f\"Invalid tag when decrypting {edata}\")\n", | ||
| 226 | "\n", | ||
| 227 | "print(f\"{encrypt('my passphrase', 'my secret')}\")\n", | ||
| 228 | "print(f\"{decrypt('my passphrase', 'enc-val$2$tmuH3MTDQ9vMbxJ1pZUb0rQlF43Lgr/AYfkA+vSOZs0=$5CVt3VrxhuuOOVt1vCXeGpbhNh/20IcFNg==')}\")" | ||
| 229 | ] | ||
| 230 | }, | ||
| 231 | { | ||
| 232 | "cell_type": "code", | ||
| 233 | "execution_count": null, | ||
| 234 | "id": "8ccfb453-d3fd-4383-a01d-f81b19f2cb82", | ||
| 235 | "metadata": {}, | ||
| 236 | "outputs": [], | ||
| 237 | "source": [] | ||
| 238 | } | ||
| 239 | ], | ||
| 240 | "metadata": { | ||
| 241 | "kernelspec": { | ||
| 242 | "display_name": "Python 3 (ipykernel)", | ||
| 243 | "language": "python", | ||
| 244 | "name": "python3" | ||
| 245 | }, | ||
| 246 | "language_info": { | ||
| 247 | "codemirror_mode": { | ||
| 248 | "name": "ipython", | ||
| 249 | "version": 3 | ||
| 250 | }, | ||
| 251 | "file_extension": ".py", | ||
| 252 | "mimetype": "text/x-python", | ||
| 253 | "name": "python", | ||
| 254 | "nbconvert_exporter": "python", | ||
| 255 | "pygments_lexer": "ipython3", | ||
| 256 | "version": "3.13.9" | ||
| 257 | } | ||
| 258 | }, | ||
| 259 | "nbformat": 4, | ||
| 260 | "nbformat_minor": 5 | ||
| 261 | } | ||
diff --git a/notebooks/python/python_3_8_to_3_11.ipynb b/notebooks/python/python_3_8_to_3_11.ipynb index b8724e8..016c108 100644 --- a/notebooks/python/python_3_8_to_3_11.ipynb +++ b/notebooks/python/python_3_8_to_3_11.ipynb | |||
| @@ -834,7 +834,7 @@ | |||
| 834 | "name": "python", | 834 | "name": "python", |
| 835 | "nbconvert_exporter": "python", | 835 | "nbconvert_exporter": "python", |
| 836 | "pygments_lexer": "ipython3", | 836 | "pygments_lexer": "ipython3", |
| 837 | "version": "3.11.7" | 837 | "version": "3.12.3" |
| 838 | } | 838 | } |
| 839 | }, | 839 | }, |
| 840 | "nbformat": 4, | 840 | "nbformat": 4, |
diff --git a/notebooks/python/python_intro.ipynb b/notebooks/python/python_intro.ipynb index f129ffe..f101400 100644 --- a/notebooks/python/python_intro.ipynb +++ b/notebooks/python/python_intro.ipynb | |||
| @@ -50,6 +50,19 @@ | |||
| 50 | }, | 50 | }, |
| 51 | { | 51 | { |
| 52 | "cell_type": "markdown", | 52 | "cell_type": "markdown", |
| 53 | "id": "779bac39-4646-4b80-9970-343a3daef1e7", | ||
| 54 | "metadata": {}, | ||
| 55 | "source": [ | ||
| 56 | "## Important PEPs\n", | ||
| 57 | "\n", | ||
| 58 | "- [PEP0](https://peps.python.org/) - Index of Python Enhancement Proposals\n", | ||
| 59 | "- [PEP8](https://peps.python.org/pep-0008/) - Style Guide for Python Code\n", | ||
| 60 | "- [PEP257](https://peps.python.org/pep-0257/) - Docstring Conventions\n", | ||
| 61 | "- [PEP484](https://peps.python.org/pep-0484/) - Type Hints" | ||
| 62 | ] | ||
| 63 | }, | ||
| 64 | { | ||
| 65 | "cell_type": "markdown", | ||
| 53 | "id": "6d1edcfc-b001-4393-9c24-72adc9b5fccf", | 66 | "id": "6d1edcfc-b001-4393-9c24-72adc9b5fccf", |
| 54 | "metadata": {}, | 67 | "metadata": {}, |
| 55 | "source": [ | 68 | "source": [ |
| @@ -72,7 +85,7 @@ | |||
| 72 | }, | 85 | }, |
| 73 | { | 86 | { |
| 74 | "cell_type": "markdown", | 87 | "cell_type": "markdown", |
| 75 | "id": "4f144f73-51c1-419d-b71e-ee513f68f41c", | 88 | "id": "81573ed5-b76e-4726-9f44-7dc017fd0896", |
| 76 | "metadata": {}, | 89 | "metadata": {}, |
| 77 | "source": [ | 90 | "source": [ |
| 78 | "## Built-in functions\n", | 91 | "## Built-in functions\n", |
| @@ -81,8 +94,6 @@ | |||
| 81 | "\n", | 94 | "\n", |
| 82 | "[https://docs.python.org/3/library/functions.html](https://docs.python.org/3/library/functions.html)\n", | 95 | "[https://docs.python.org/3/library/functions.html](https://docs.python.org/3/library/functions.html)\n", |
| 83 | "\n", | 96 | "\n", |
| 84 | "- `dir([obj])` - returns a list of valid attributes for that object\n", | ||
| 85 | "\n", | ||
| 86 | "- `id(obj)` - returns the \"identity\" of an object - an integer which is guaranteed to be unique\n", | 97 | "- `id(obj)` - returns the \"identity\" of an object - an integer which is guaranteed to be unique\n", |
| 87 | "\n", | 98 | "\n", |
| 88 | "- `print(...)` - prints objects to a text stream\n", | 99 | "- `print(...)` - prints objects to a text stream\n", |
| @@ -94,6 +105,12 @@ | |||
| 94 | }, | 105 | }, |
| 95 | { | 106 | { |
| 96 | "cell_type": "markdown", | 107 | "cell_type": "markdown", |
| 108 | "id": "bc180243-fe11-405c-beb9-83db4f793a05", | ||
| 109 | "metadata": {}, | ||
| 110 | "source": [] | ||
| 111 | }, | ||
| 112 | { | ||
| 113 | "cell_type": "markdown", | ||
| 97 | "id": "a0c57535-e285-47af-80f9-1d4a16de5f25", | 114 | "id": "a0c57535-e285-47af-80f9-1d4a16de5f25", |
| 98 | "metadata": {}, | 115 | "metadata": {}, |
| 99 | "source": [ | 116 | "source": [ |
| @@ -116,26 +133,28 @@ | |||
| 116 | "source": [ | 133 | "source": [ |
| 117 | "# Common built-in types:\n", | 134 | "# Common built-in types:\n", |
| 118 | "\n", | 135 | "\n", |
| 119 | "s = 'foo' # this is a string / str, same as str('foo'), may be encoded, immutable (s[0] = 'r' is NOT possible)\n", | 136 | "my_string = 'foo' # a string / str, same as str('foo'), may be encoded\n", |
| 137 | " # immutable (s[0] = 'r' is NOT possible)\n", | ||
| 120 | "\n", | 138 | "\n", |
| 121 | "b = b'foo' # bytes, same as bytes('foo', 'utf-8'), may be decoded, immutable\n", | 139 | "my_bytes = b'foo' # bytes, same as bytes('foo', 'utf-8'), may be decoded, immutable\n", |
| 122 | "\n", | 140 | "\n", |
| 123 | "i = 6 # int, same as int('6'), immutable\n", | 141 | "my_int = 6 # int, same as int('6'), immutable\n", |
| 124 | "\n", | 142 | "\n", |
| 125 | "f = 0.1 # float, same as float('0.1'), immutable, Note!!: Floats have a fixed size,\n", | 143 | "my_float = 0.1 # float, same as float('0.1'), immutable, Note!!: Floats have a fixed size,\n", |
| 126 | "# hence they don't necessarily behave they way we expect from math class.\n", | 144 | " # hence they don't necessarily behave they way we expect from math class.\n", |
| 145 | "# 24732847234232342892343428.3 == 24732847234232342892343428.1 # True\n", | ||
| 127 | "\n", | 146 | "\n", |
| 128 | "b = False # bool, same as bool(0), bool(''), bool(None)... immutable / constant\n", | 147 | "my_bool = False # bool, same as bool(0), bool(''), bool(None)... immutable / constant\n", |
| 129 | "\n", | 148 | "\n", |
| 130 | "n = None # NoneType, similar to 'null' in other languages, immutable / constant\n", | 149 | "my_none = None # NoneType, similar to 'null' in other languages, immutable / constant\n", |
| 131 | "\n", | 150 | "\n", |
| 132 | "l = [1, False, 'foo'] # list, same as list((1, False, 'foo'))\n", | 151 | "my_list = [1, False, 'foo'] # list, same as list((1, False, 'foo'))\n", |
| 133 | "\n", | 152 | "\n", |
| 134 | "t = (1, False, 'foo') # tuple, same as tuple([1, False, 'foo']), immutable\n", | 153 | "my_tuple = (1, False, 'foo') # tuple, same as tuple([1, False, 'foo']), immutable\n", |
| 135 | "\n", | 154 | "\n", |
| 136 | "d = {'foo': 1, 'bar': 8} # dict, same as dict(foo=1, bar=8), similar to hash in other languages\n", | 155 | "my_dict = {'foo': 1, 'bar': 8} # dict, same as dict(foo=1, bar=8), similar to hash in other languages\n", |
| 137 | "\n", | 156 | "\n", |
| 138 | "s = {'foo', 'bar', 1, 1, 4} # set, same as set(['foo', 'bar', 1, 1, 4]), removes duplicates" | 157 | "my_set = {'foo', 'bar', 1, 1, 4} # set, same as set(['foo', 'bar', 1, 1, 4]), removes duplicates" |
| 139 | ] | 158 | ] |
| 140 | }, | 159 | }, |
| 141 | { | 160 | { |
| @@ -203,7 +222,7 @@ | |||
| 203 | "s3[0] # Out: S\n", | 222 | "s3[0] # Out: S\n", |
| 204 | "s3[-1] # Out: t\n", | 223 | "s3[-1] # Out: t\n", |
| 205 | "s3[1:] # Out: tatnett\n", | 224 | "s3[1:] # Out: tatnett\n", |
| 206 | "s3[1:-1] # Out: tatnett\n", | 225 | "s3[1:-1] # Out: tatnet\n", |
| 207 | "s3[-3:] # Out: ett\n", | 226 | "s3[-3:] # Out: ett\n", |
| 208 | "s3[1:-1:2] # Out: tte\n", | 227 | "s3[1:-1:2] # Out: tte\n", |
| 209 | "\n", | 228 | "\n", |
| @@ -223,6 +242,29 @@ | |||
| 223 | }, | 242 | }, |
| 224 | { | 243 | { |
| 225 | "cell_type": "markdown", | 244 | "cell_type": "markdown", |
| 245 | "id": "25d0f9dc-d783-4a23-a3ee-c0b6cee3d4ab", | ||
| 246 | "metadata": {}, | ||
| 247 | "source": [ | ||
| 248 | "## Lists vs. tuples\n", | ||
| 249 | "\n", | ||
| 250 | "Tuples are not sumply \"read-only\" lists.\n", | ||
| 251 | "\n", | ||
| 252 | "\"Though tuples may seem similar to lists, they are often used in different situations and for different purposes.\n", | ||
| 253 | "Tuples are immutable, and usually contain a heterogeneous sequence of elements that are accessed via unpacking ... or indexing.\n", | ||
| 254 | "Lists are mutable, and their elements are usually homogeneous and are accessed by iterating over the list.\n", | ||
| 255 | "\n", | ||
| 256 | "```python\n", | ||
| 257 | "my_tuple = (1,2)\n", | ||
| 258 | "my_list = [1,2] \n", | ||
| 259 | "\n", | ||
| 260 | "# tuples are immutable and hashable, hence they can be used as keys in mapping objects as dicts\n", | ||
| 261 | "d = {my_tuple: 1} # OK\n", | ||
| 262 | "d = {my_list: 1} # Error\n", | ||
| 263 | "```" | ||
| 264 | ] | ||
| 265 | }, | ||
| 266 | { | ||
| 267 | "cell_type": "markdown", | ||
| 226 | "id": "6b2bc8fa-a5dc-4a58-92b2-abfb19bfe2ed", | 268 | "id": "6b2bc8fa-a5dc-4a58-92b2-abfb19bfe2ed", |
| 227 | "metadata": {}, | 269 | "metadata": {}, |
| 228 | "source": [ | 270 | "source": [ |
| @@ -302,7 +344,7 @@ | |||
| 302 | "id": "40db50e4-fb7a-4a77-a074-bc4413f27212", | 344 | "id": "40db50e4-fb7a-4a77-a074-bc4413f27212", |
| 303 | "metadata": {}, | 345 | "metadata": {}, |
| 304 | "source": [ | 346 | "source": [ |
| 305 | "## Creating and maintaining a Python environment (cont...)\n", | 347 | "## Creating and maintaining a Python environment - goals\n", |
| 306 | "\n", | 348 | "\n", |
| 307 | "Desired qualities for a flexible Python environment:\n", | 349 | "Desired qualities for a flexible Python environment:\n", |
| 308 | "\n", | 350 | "\n", |
| @@ -324,10 +366,11 @@ | |||
| 324 | "id": "c661bf56-add0-402c-a629-a857f81d0758", | 366 | "id": "c661bf56-add0-402c-a629-a857f81d0758", |
| 325 | "metadata": {}, | 367 | "metadata": {}, |
| 326 | "source": [ | 368 | "source": [ |
| 327 | "## Creating and maintaining a Python environment (cont...)\n", | 369 | "## Creating and maintaining a Python environment - using devbox / WSL / UNIX systems directly\n", |
| 328 | "\n", | 370 | "\n", |
| 329 | "Exploting the operating system can be done by:\n", | 371 | "Exploting the operating system can be done by:\n", |
| 330 | "\n", | 372 | "\n", |
| 373 | "- using the official Python packages that are maintained by the OS (Ubuntu packages in the case of WSL @ Statnett or a Red Hat enterprise VM @ Statnett) is often good enough\n", | ||
| 331 | "- (re)defining `PYTHONPATH`\n", | 374 | "- (re)defining `PYTHONPATH`\n", |
| 332 | "- using symlinks to point at packages placed at different locations" | 375 | "- using symlinks to point at packages placed at different locations" |
| 333 | ] | 376 | ] |
| @@ -337,7 +380,7 @@ | |||
| 337 | "id": "db8952aa-0944-4644-9626-7460786fd72a", | 380 | "id": "db8952aa-0944-4644-9626-7460786fd72a", |
| 338 | "metadata": {}, | 381 | "metadata": {}, |
| 339 | "source": [ | 382 | "source": [ |
| 340 | "## Creating and maintaining a Python environment (cont...)\n", | 383 | "## Creating and maintaining a Python environment - using the Python virtual environment - *venv*\n", |
| 341 | "\n", | 384 | "\n", |
| 342 | "Using venv can be done by directly invoking python:\n", | 385 | "Using venv can be done by directly invoking python:\n", |
| 343 | "\n", | 386 | "\n", |
| @@ -365,10 +408,13 @@ | |||
| 365 | "id": "299641e6-3d87-4a86-a124-eb49edb75b4b", | 408 | "id": "299641e6-3d87-4a86-a124-eb49edb75b4b", |
| 366 | "metadata": {}, | 409 | "metadata": {}, |
| 367 | "source": [ | 410 | "source": [ |
| 368 | "## Creating and maintaining a Python environment (cont...)\n", | 411 | "## Creating and maintaining a Python environment - using Poetry\n", |
| 369 | "\n", | 412 | "\n", |
| 370 | "Poetry [https://python-poetry.org](https://python-poetry.org) is the prefered environment and dependency management tool at Statnett.\n", | 413 | "Poetry [https://python-poetry.org](https://python-poetry.org) is the prefered environment and dependency management tool at Statnett.\n", |
| 371 | "\n", | 414 | "\n", |
| 415 | "Although Poetry creates and uses vierual environment(s) behind the scenes,\n", | ||
| 416 | "it is centered around the concept of \"projects\" and not the virtual environment itself\n", | ||
| 417 | "\n", | ||
| 372 | "```bash\n", | 418 | "```bash\n", |
| 373 | "# create project and a virtual environment from scratch\n", | 419 | "# create project and a virtual environment from scratch\n", |
| 374 | "poetry new my-project\n", | 420 | "poetry new my-project\n", |
| @@ -411,7 +457,7 @@ | |||
| 411 | { | 457 | { |
| 412 | "data": { | 458 | "data": { |
| 413 | "text/plain": [ | 459 | "text/plain": [ |
| 414 | "-42990858669078524" | 460 | "-3158428850515418558" |
| 415 | ] | 461 | ] |
| 416 | }, | 462 | }, |
| 417 | "execution_count": 15, | 463 | "execution_count": 15, |
| @@ -435,8 +481,8 @@ | |||
| 435 | "hash(t) # returns f.i. -6333845781340707986\n", | 481 | "hash(t) # returns f.i. -6333845781340707986\n", |
| 436 | "# hash(l) # TypeError: unhashable type: 'list'\n", | 482 | "# hash(l) # TypeError: unhashable type: 'list'\n", |
| 437 | "\n", | 483 | "\n", |
| 438 | "s = 'the long and winding road'\n", | 484 | "s = \"the long and winding road\"\n", |
| 439 | "s2 = 'the long and winding road'\n", | 485 | "s2 = \"the long and winding road\"\n", |
| 440 | "\n", | 486 | "\n", |
| 441 | "# check if s and s2 are the same object:\n", | 487 | "# check if s and s2 are the same object:\n", |
| 442 | "id(s) # Out: 139858905258704\n", | 488 | "id(s) # Out: 139858905258704\n", |
| @@ -816,7 +862,7 @@ | |||
| 816 | "# the function will behave differently depending on what parameters were\n", | 862 | "# the function will behave differently depending on what parameters were\n", |
| 817 | "# used when calling its enclosing function (factory function)\n", | 863 | "# used when calling its enclosing function (factory function)\n", |
| 818 | "\n", | 864 | "\n", |
| 819 | "def get_multiplier_of(base: int) -> str:\n", | 865 | "def get_multiplier_of(base: int):\n", |
| 820 | " \"\"\"the function enclosing its nested functions\"\"\"\n", | 866 | " \"\"\"the function enclosing its nested functions\"\"\"\n", |
| 821 | " # this function is the enclosing function of the function `multiplier_function`\n", | 867 | " # this function is the enclosing function of the function `multiplier_function`\n", |
| 822 | "\n", | 868 | "\n", |
| @@ -958,19 +1004,7 @@ | |||
| 958 | "execution_count": 22, | 1004 | "execution_count": 22, |
| 959 | "id": "4346260d-76af-437e-9c10-1c1d0c478695", | 1005 | "id": "4346260d-76af-437e-9c10-1c1d0c478695", |
| 960 | "metadata": {}, | 1006 | "metadata": {}, |
| 961 | "outputs": [ | 1007 | "outputs": [], |
| 962 | { | ||
| 963 | "ename": "NameError", | ||
| 964 | "evalue": "name 'is_admin' is not defined", | ||
| 965 | "output_type": "error", | ||
| 966 | "traceback": [ | ||
| 967 | "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", | ||
| 968 | "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", | ||
| 969 | "Cell \u001b[0;32mIn[22], line 25\u001b[0m\n\u001b[1;32m 19\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m decorated\n\u001b[1;32m 21\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m api_access_decorator\n\u001b[1;32m 24\u001b[0m \u001b[38;5;129m@requires_access\u001b[39m(access_secret\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mb28cfeaa65b73cf\u001b[39m\u001b[38;5;124m'\u001b[39m)\n\u001b[0;32m---> 25\u001b[0m \u001b[38;5;129m@is_admin\u001b[39m\n\u001b[1;32m 26\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21msensitive_function\u001b[39m(data, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs):\n\u001b[1;32m 27\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m\"\"\"very sensitive function\"\"\"\u001b[39;00m\n\u001b[1;32m 28\u001b[0m db\u001b[38;5;241m.\u001b[39msave(data)\n", | ||
| 970 | "\u001b[0;31mNameError\u001b[0m: name 'is_admin' is not defined" | ||
| 971 | ] | ||
| 972 | } | ||
| 973 | ], | ||
| 974 | "source": [ | 1008 | "source": [ |
| 975 | "# Decorators (cont ...) - a complete example\n", | 1009 | "# Decorators (cont ...) - a complete example\n", |
| 976 | "\n", | 1010 | "\n", |
| @@ -996,7 +1030,7 @@ | |||
| 996 | "\n", | 1030 | "\n", |
| 997 | "\n", | 1031 | "\n", |
| 998 | "@requires_access(access_secret='b28cfeaa65b73cf')\n", | 1032 | "@requires_access(access_secret='b28cfeaa65b73cf')\n", |
| 999 | "@is_admin\n", | 1033 | "# @is_admin - decorators can be \"chained\"\n", |
| 1000 | "def sensitive_function(data, **kwargs):\n", | 1034 | "def sensitive_function(data, **kwargs):\n", |
| 1001 | " \"\"\"very sensitive function\"\"\"\n", | 1035 | " \"\"\"very sensitive function\"\"\"\n", |
| 1002 | " db.save(data)" | 1036 | " db.save(data)" |
| @@ -1030,11 +1064,19 @@ | |||
| 1030 | "\n", | 1064 | "\n", |
| 1031 | "\n", | 1065 | "\n", |
| 1032 | "# modern Python >= 3.6 f-strings\n", | 1066 | "# modern Python >= 3.6 f-strings\n", |
| 1033 | "f'{s} - {i} - {f:5.2f}' # Out: 'another string - 27 - 6.58'\n", | 1067 | "f\"{s} - {i} - {f:5.2f}\" # Out: 'another string - 27 - 6.58'\n", |
| 1034 | "``` \n", | 1068 | "``` \n", |
| 1035 | "\n", | 1069 | "\n", |
| 1036 | "See https://docs.python.org/3/library/string.html#formatspec for the complete format specification" | 1070 | "See https://docs.python.org/3/library/string.html#formatspec for the complete format specification" |
| 1037 | ] | 1071 | ] |
| 1072 | }, | ||
| 1073 | { | ||
| 1074 | "cell_type": "code", | ||
| 1075 | "execution_count": null, | ||
| 1076 | "id": "23972592-e838-4fa7-81f1-ca3ca129e757", | ||
| 1077 | "metadata": {}, | ||
| 1078 | "outputs": [], | ||
| 1079 | "source": [] | ||
| 1038 | } | 1080 | } |
| 1039 | ], | 1081 | ], |
| 1040 | "metadata": { | 1082 | "metadata": { |
| @@ -1053,7 +1095,7 @@ | |||
| 1053 | "name": "python", | 1095 | "name": "python", |
| 1054 | "nbconvert_exporter": "python", | 1096 | "nbconvert_exporter": "python", |
| 1055 | "pygments_lexer": "ipython3", | 1097 | "pygments_lexer": "ipython3", |
| 1056 | "version": "3.11.7" | 1098 | "version": "3.13.9" |
| 1057 | } | 1099 | } |
| 1058 | }, | 1100 | }, |
| 1059 | "nbformat": 4, | 1101 | "nbformat": 4, |
diff --git a/notebooks/python/python_oo.ipynb b/notebooks/python/python_oo.ipynb index e38d7f3..1ef2a72 100644 --- a/notebooks/python/python_oo.ipynb +++ b/notebooks/python/python_oo.ipynb | |||
| @@ -66,7 +66,55 @@ | |||
| 66 | "\n", | 66 | "\n", |
| 67 | "- improving modularity\n", | 67 | "- improving modularity\n", |
| 68 | "\n", | 68 | "\n", |
| 69 | "- providing foundation for a more intuitive design" | 69 | "- providing foundation for a more intuitive design\n", |
| 70 | "\n", | ||
| 71 | "A pseudo code example:\n", | ||
| 72 | "\n", | ||
| 73 | "```c\n", | ||
| 74 | "/* crating and drawing a (pseudo) widget in C */\n", | ||
| 75 | "my_widget = mylibrary_widget_init(); /* a struct of the type widget */\n", | ||
| 76 | "mylibrary_widget_draw(my_widget);\n", | ||
| 77 | "```\n", | ||
| 78 | "\n", | ||
| 79 | "```python\n", | ||
| 80 | "# creating and drawing a (pseudo) widget in Python\n", | ||
| 81 | "import mylibrary\n", | ||
| 82 | "\n", | ||
| 83 | "my_widget = mylibrary.Widget()\n", | ||
| 84 | "my_widget.draw()\n", | ||
| 85 | "```" | ||
| 86 | ] | ||
| 87 | }, | ||
| 88 | { | ||
| 89 | "cell_type": "markdown", | ||
| 90 | "id": "878cb805-a2d7-46a3-a34a-0507495420f7", | ||
| 91 | "metadata": {}, | ||
| 92 | "source": [ | ||
| 93 | "# Do I need to know / understand OOP when I am programming in Python?\n", | ||
| 94 | "\n", | ||
| 95 | "Most definitely - **yes**.\n", | ||
| 96 | "\n", | ||
| 97 | "While using programming language that is not object-oriented (like *C* and / or *Rust*) is always possible,\n", | ||
| 98 | "when using Python there are no *sane* alternatives that have ever been demonstrated:\n", | ||
| 99 | "\n", | ||
| 100 | "- Understanding OO behaviour and syntax is a must in order to understand Python code in general\n", | ||
| 101 | "- OOP is at the core of Python's design - everything in Python is an object\n", | ||
| 102 | "- Good programming and design is not only about solving a particular task. It is about reusing code and making your code reusable in turn\n", | ||
| 103 | "- Following the priciples described above is more challenging when not using OO in general." | ||
| 104 | ] | ||
| 105 | }, | ||
| 106 | { | ||
| 107 | "cell_type": "markdown", | ||
| 108 | "id": "b83dc815-ed51-4e5a-a30e-fa0252217194", | ||
| 109 | "metadata": {}, | ||
| 110 | "source": [ | ||
| 111 | "# I am doing only simple things with Python. Do I still need to learn OOP?\n", | ||
| 112 | "\n", | ||
| 113 | "Most definitely - **yes**.\n", | ||
| 114 | "\n", | ||
| 115 | "- A simple task provides a great opportunity to learn and practice OOP\n", | ||
| 116 | "- Using OOP **does not mean overcomplicating** your code. When applied correctly - it means the opposite\n", | ||
| 117 | "- Doing \"simple things\" exclusively is not very ambitious approach to programming and learning in general" | ||
| 70 | ] | 118 | ] |
| 71 | }, | 119 | }, |
| 72 | { | 120 | { |
| @@ -109,13 +157,20 @@ | |||
| 109 | "\n", | 157 | "\n", |
| 110 | "- object - an instance of a class that may contain its own attributes as well as references to its class' attributes\n", | 158 | "- object - an instance of a class that may contain its own attributes as well as references to its class' attributes\n", |
| 111 | "\n", | 159 | "\n", |
| 112 | "- attribute - variable, property, function defined in the class and present in its instances\n", | 160 | "- attribute - variable, property, function defined in the class and present in its instances - in Python: any name following a dot. Attributes may be *read-only* or *writable*. In the latter case, assignment to attributes is possible.\n", |
| 113 | "\n", | 161 | "\n", |
| 114 | "- class variable - attribute of which a single copy exists, regardless of how many instances of the class exist\n", | 162 | "- class variable - attribute of which a single copy exists, regardless of how many instances of the class exist\n", |
| 115 | "\n", | 163 | "\n", |
| 116 | "- object / instance variable, object / instance attribute - attribute for which each instantiated object of the class has a separate copy, or instance\n", | 164 | "- object / instance variable, object / instance attribute - attribute for which each instantiated object of the class has a separate copy, or instance\n", |
| 117 | "\n", | 165 | "\n", |
| 118 | "- method - member function - function that is an attribute" | 166 | "- method - member function - function that is an attribute\n", |
| 167 | "\n", | ||
| 168 | "```python\n", | ||
| 169 | "# my_str_obj is an object (instance) of the class str\n", | ||
| 170 | "my_str_object = str(\"Test\") # or my_str_object = \"Test\"\n", | ||
| 171 | "my_str_object.lower # attribute that is a function -> method\n", | ||
| 172 | "my_str_object.lower() # calling the method \"lower\"\n", | ||
| 173 | "```" | ||
| 119 | ] | 174 | ] |
| 120 | }, | 175 | }, |
| 121 | { | 176 | { |
| @@ -218,7 +273,7 @@ | |||
| 218 | }, | 273 | }, |
| 219 | { | 274 | { |
| 220 | "cell_type": "code", | 275 | "cell_type": "code", |
| 221 | "execution_count": 1, | 276 | "execution_count": 41, |
| 222 | "id": "a9c30a5d-aa86-45ab-b897-f051df80f1d8", | 277 | "id": "a9c30a5d-aa86-45ab-b897-f051df80f1d8", |
| 223 | "metadata": {}, | 278 | "metadata": {}, |
| 224 | "outputs": [ | 279 | "outputs": [ |
| @@ -226,17 +281,17 @@ | |||
| 226 | "name": "stdout", | 281 | "name": "stdout", |
| 227 | "output_type": "stream", | 282 | "output_type": "stream", |
| 228 | "text": [ | 283 | "text": [ |
| 229 | "car1.get_obj_info_str() = \"I am <__main__.Car object at 0x7f70e643d590> with id 140122876269968 from <class '__main__.Car'> with id 93964809433504\"\n", | 284 | "car1.get_obj_info_str() = \"I am <__main__.Car object at 0x7fd00029ff80> with id 140531332677504 from <class '__main__.Car'> with id 93887620597168\"\n", |
| 230 | "car2.get_obj_info_str() = \"I am <__main__.Car object at 0x7f70e63ddf10> with id 140122875879184 from <class '__main__.Car'> with id 93964809433504\"\n", | 285 | "car2.get_obj_info_str() = \"I am <__main__.Car object at 0x7fd00029ea50> with id 140531332672080 from <class '__main__.Car'> with id 93887620597168\"\n", |
| 231 | "car1.model = 'BMW', car1.reg_nr = 'EC76183', car1.extras = ['GPSnav', 'Sound system'], id(car1.cls_extras) = 140122875882432, id(car1.get_obj_info_str) = 140122875878080\n", | 286 | "car1.model = 'BMW', car1.reg_nr = 'EC76183', car1.extras = ['GPSnav', 'Sound system'], id(car1.cls_extras) = 140531337557440, id(car1.get_obj_info_str) = 140531251735488\n", |
| 232 | "car2.model = 'Scoda', car2.reg_nr = 'BD77655', car2.extras = ['GPSnav'], id(car2.cls_extras) = 140122875882432, id(car2.get_obj_info_str) = 140122875880192\n", | 287 | "car2.model = 'Scoda', car2.reg_nr = 'BD77655', car2.extras = ['GPSnav'], id(car2.cls_extras) = 140531337557440, id(car2.get_obj_info_str) = 140531251728064\n", |
| 233 | "id(Car.cls_extras) = 140122875882432, id(Car.get_obj_info_str) = 140122875964416\n", | 288 | "id(Car.cls_extras) = 140531337557440, id(Car.get_obj_info_str) = 140531252010400\n", |
| 234 | "car1.cls_extras = ['GPSnav', 'Sound system'], id(car1.cls_extras) = 140122875882432\n", | 289 | "car1.cls_extras = ['GPSnav', 'Sound system'], id(car1.cls_extras) = 140531337557440\n", |
| 235 | "car2.cls_extras = ['GPSnav', 'Sound system'], id(car2.cls_extras) = 140122875882432\n", | 290 | "car2.cls_extras = ['GPSnav', 'Sound system'], id(car2.cls_extras) = 140531337557440\n", |
| 236 | "True\n", | 291 | "True\n", |
| 237 | "hasattr(car1, 'import_tax_paid') = True\n", | 292 | "hasattr(car1, 'import_tax_paid') = True\n", |
| 238 | "hasattr(car2, 'import_tax_paid') = False\n", | 293 | "hasattr(car2, 'import_tax_paid') = False\n", |
| 239 | "id(car1.__class__) = 93964809433504, id(car2.__class__) = 93964809433504, id(Car) = 93964809433504\n" | 294 | "id(car1.__class__) = 93887620597168, id(car2.__class__) = 93887620597168, id(Car) = 93887620597168\n" |
| 240 | ] | 295 | ] |
| 241 | } | 296 | } |
| 242 | ], | 297 | ], |
| @@ -288,7 +343,7 @@ | |||
| 288 | "# - checks if 'attr' is an instance attribute\n", | 343 | "# - checks if 'attr' is an instance attribute\n", |
| 289 | "# - checks if 'attr' is a class attribute (through the method resolution order - MRO)\n", | 344 | "# - checks if 'attr' is a class attribute (through the method resolution order - MRO)\n", |
| 290 | "# - raises AttributeError\n", | 345 | "# - raises AttributeError\n", |
| 291 | "# setter:\n", | 346 | "# setter (making the attribute writable):\n", |
| 292 | "# - (re)defines an instance attribute\n", | 347 | "# - (re)defines an instance attribute\n", |
| 293 | "\n", | 348 | "\n", |
| 294 | "# Details not covered in this course:\n", | 349 | "# Details not covered in this course:\n", |
| @@ -323,7 +378,7 @@ | |||
| 323 | }, | 378 | }, |
| 324 | { | 379 | { |
| 325 | "cell_type": "code", | 380 | "cell_type": "code", |
| 326 | "execution_count": 2, | 381 | "execution_count": 42, |
| 327 | "id": "391e816e-c53b-454b-9e9f-3587234f1fea", | 382 | "id": "391e816e-c53b-454b-9e9f-3587234f1fea", |
| 328 | "metadata": {}, | 383 | "metadata": {}, |
| 329 | "outputs": [ | 384 | "outputs": [ |
| @@ -413,7 +468,7 @@ | |||
| 413 | }, | 468 | }, |
| 414 | { | 469 | { |
| 415 | "cell_type": "code", | 470 | "cell_type": "code", |
| 416 | "execution_count": 3, | 471 | "execution_count": 43, |
| 417 | "id": "e82feefb-025d-4cbb-a8ac-ddc3cc01269c", | 472 | "id": "e82feefb-025d-4cbb-a8ac-ddc3cc01269c", |
| 418 | "metadata": {}, | 473 | "metadata": {}, |
| 419 | "outputs": [ | 474 | "outputs": [ |
| @@ -495,7 +550,7 @@ | |||
| 495 | }, | 550 | }, |
| 496 | { | 551 | { |
| 497 | "cell_type": "code", | 552 | "cell_type": "code", |
| 498 | "execution_count": 4, | 553 | "execution_count": 44, |
| 499 | "id": "71a90ec6-3cc0-441e-a866-c2c2b9984559", | 554 | "id": "71a90ec6-3cc0-441e-a866-c2c2b9984559", |
| 500 | "metadata": {}, | 555 | "metadata": {}, |
| 501 | "outputs": [ | 556 | "outputs": [ |
| @@ -613,7 +668,7 @@ | |||
| 613 | }, | 668 | }, |
| 614 | { | 669 | { |
| 615 | "cell_type": "code", | 670 | "cell_type": "code", |
| 616 | "execution_count": 5, | 671 | "execution_count": 45, |
| 617 | "id": "a4d2735d-d167-4f43-ad55-cb3d13ece2dc", | 672 | "id": "a4d2735d-d167-4f43-ad55-cb3d13ece2dc", |
| 618 | "metadata": {}, | 673 | "metadata": {}, |
| 619 | "outputs": [ | 674 | "outputs": [ |
| @@ -656,7 +711,7 @@ | |||
| 656 | }, | 711 | }, |
| 657 | { | 712 | { |
| 658 | "cell_type": "code", | 713 | "cell_type": "code", |
| 659 | "execution_count": 6, | 714 | "execution_count": 46, |
| 660 | "id": "a6ead5e6-e987-4bda-bf48-30f91877278d", | 715 | "id": "a6ead5e6-e987-4bda-bf48-30f91877278d", |
| 661 | "metadata": {}, | 716 | "metadata": {}, |
| 662 | "outputs": [ | 717 | "outputs": [ |
| @@ -715,7 +770,7 @@ | |||
| 715 | }, | 770 | }, |
| 716 | { | 771 | { |
| 717 | "cell_type": "code", | 772 | "cell_type": "code", |
| 718 | "execution_count": 7, | 773 | "execution_count": 47, |
| 719 | "id": "11f6a3fb-64d7-40a9-a130-27548ec4b802", | 774 | "id": "11f6a3fb-64d7-40a9-a130-27548ec4b802", |
| 720 | "metadata": {}, | 775 | "metadata": {}, |
| 721 | "outputs": [ | 776 | "outputs": [ |
| @@ -797,7 +852,7 @@ | |||
| 797 | }, | 852 | }, |
| 798 | { | 853 | { |
| 799 | "cell_type": "code", | 854 | "cell_type": "code", |
| 800 | "execution_count": 8, | 855 | "execution_count": 48, |
| 801 | "id": "c345a69f-02da-40e7-bb37-c0511b6af096", | 856 | "id": "c345a69f-02da-40e7-bb37-c0511b6af096", |
| 802 | "metadata": {}, | 857 | "metadata": {}, |
| 803 | "outputs": [ | 858 | "outputs": [ |
| @@ -806,6 +861,9 @@ | |||
| 806 | "output_type": "stream", | 861 | "output_type": "stream", |
| 807 | "text": [ | 862 | "text": [ |
| 808 | "9\n", | 863 | "9\n", |
| 864 | "9\n", | ||
| 865 | "140531251904896\n", | ||
| 866 | "140531251904896\n", | ||
| 809 | "Vector.from_str('1:1:5:6') = Vector(start=Point(x=1, y=1), end=Point(x=5, y=6))\n" | 867 | "Vector.from_str('1:1:5:6') = Vector(start=Point(x=1, y=1), end=Point(x=5, y=6))\n" |
| 810 | ] | 868 | ] |
| 811 | } | 869 | } |
| @@ -837,7 +895,14 @@ | |||
| 837 | " Point(coordinates[2], coordinates[3]),\n", | 895 | " Point(coordinates[2], coordinates[3]),\n", |
| 838 | " )\n", | 896 | " )\n", |
| 839 | "\n", | 897 | "\n", |
| 840 | "print(Point.get_manhattan_distance(Point(1, 1), Point(5, 6)))\n", | 898 | "point1 = Point(1, 1)\n", |
| 899 | "point2 = Point(5, 6)\n", | ||
| 900 | "print(f\"{point1.get_manhattan_distance(point1, point2)}\") # works, but should not be used in that way\n", | ||
| 901 | "print(f\"{Point.get_manhattan_distance(point1, point2)}\")\n", | ||
| 902 | "print(f\"{id(point1.get_manhattan_distance)}\")\n", | ||
| 903 | "print(f\"{id(point2.get_manhattan_distance)}\")\n", | ||
| 904 | "# print(f\"{id(point1.from_str)}\") <-- not available as an instance attribute\n", | ||
| 905 | "\n", | ||
| 841 | "print(f\"{Vector.from_str('1:1:5:6') = }\")" | 906 | "print(f\"{Vector.from_str('1:1:5:6') = }\")" |
| 842 | ] | 907 | ] |
| 843 | }, | 908 | }, |
| @@ -890,7 +955,7 @@ | |||
| 890 | }, | 955 | }, |
| 891 | { | 956 | { |
| 892 | "cell_type": "code", | 957 | "cell_type": "code", |
| 893 | "execution_count": 9, | 958 | "execution_count": 49, |
| 894 | "id": "44d47e1c-1a06-4577-8db6-6ec78ce5cef8", | 959 | "id": "44d47e1c-1a06-4577-8db6-6ec78ce5cef8", |
| 895 | "metadata": {}, | 960 | "metadata": {}, |
| 896 | "outputs": [ | 961 | "outputs": [ |
| @@ -903,32 +968,32 @@ | |||
| 903 | "\n", | 968 | "\n", |
| 904 | "class TigerShark(Fish)\n", | 969 | "class TigerShark(Fish)\n", |
| 905 | " | TigerShark(weight: int, alive: bool = True, **kwargs)\n", | 970 | " | TigerShark(weight: int, alive: bool = True, **kwargs)\n", |
| 906 | " | \n", | 971 | " |\n", |
| 907 | " | Base class for all tiger sharks\n", | 972 | " | Base class for all tiger sharks\n", |
| 908 | " | \n", | 973 | " |\n", |
| 909 | " | Method resolution order:\n", | 974 | " | Method resolution order:\n", |
| 910 | " | TigerShark\n", | 975 | " | TigerShark\n", |
| 911 | " | Fish\n", | 976 | " | Fish\n", |
| 912 | " | Animal\n", | 977 | " | Animal\n", |
| 913 | " | builtins.object\n", | 978 | " | builtins.object\n", |
| 914 | " | \n", | 979 | " |\n", |
| 915 | " | Methods defined here:\n", | 980 | " | Methods defined here:\n", |
| 916 | " | \n", | 981 | " |\n", |
| 917 | " | __init__(self, weight: int, alive: bool = True, **kwargs)\n", | 982 | " | __init__(self, weight: int, alive: bool = True, **kwargs)\n", |
| 918 | " | Initialize self. See help(type(self)) for accurate signature.\n", | 983 | " | Initialize self. See help(type(self)) for accurate signature.\n", |
| 919 | " | \n", | 984 | " |\n", |
| 920 | " | ----------------------------------------------------------------------\n", | 985 | " | ----------------------------------------------------------------------\n", |
| 921 | " | Data descriptors inherited from Animal:\n", | 986 | " | Data descriptors inherited from Animal:\n", |
| 922 | " | \n", | 987 | " |\n", |
| 923 | " | __dict__\n", | 988 | " | __dict__\n", |
| 924 | " | dictionary for instance variables (if defined)\n", | 989 | " | dictionary for instance variables\n", |
| 925 | " | \n", | 990 | " |\n", |
| 926 | " | __weakref__\n", | 991 | " | __weakref__\n", |
| 927 | " | list of weak references to the object (if defined)\n", | 992 | " | list of weak references to the object\n", |
| 928 | " | \n", | 993 | " |\n", |
| 929 | " | alive\n", | 994 | " | alive\n", |
| 930 | " | getter property alive\n", | 995 | " | getter property alive\n", |
| 931 | " | \n", | 996 | " |\n", |
| 932 | " | weight\n", | 997 | " | weight\n", |
| 933 | " | getter property weight\n", | 998 | " | getter property weight\n", |
| 934 | "\n", | 999 | "\n", |
| @@ -1064,7 +1129,7 @@ | |||
| 1064 | }, | 1129 | }, |
| 1065 | { | 1130 | { |
| 1066 | "cell_type": "code", | 1131 | "cell_type": "code", |
| 1067 | "execution_count": 10, | 1132 | "execution_count": 50, |
| 1068 | "id": "e6076925-75f0-456c-aed5-7db0a24a67a1", | 1133 | "id": "e6076925-75f0-456c-aed5-7db0a24a67a1", |
| 1069 | "metadata": {}, | 1134 | "metadata": {}, |
| 1070 | "outputs": [ | 1135 | "outputs": [ |
| @@ -1169,7 +1234,7 @@ | |||
| 1169 | }, | 1234 | }, |
| 1170 | { | 1235 | { |
| 1171 | "cell_type": "code", | 1236 | "cell_type": "code", |
| 1172 | "execution_count": 11, | 1237 | "execution_count": 51, |
| 1173 | "id": "cf79617a-95d2-48f2-be66-fa1fa34e4e04", | 1238 | "id": "cf79617a-95d2-48f2-be66-fa1fa34e4e04", |
| 1174 | "metadata": {}, | 1239 | "metadata": {}, |
| 1175 | "outputs": [ | 1240 | "outputs": [ |
| @@ -1192,14 +1257,14 @@ | |||
| 1192 | " | Adam\n", | 1257 | " | Adam\n", |
| 1193 | " | Eve\n", | 1258 | " | Eve\n", |
| 1194 | " | builtins.object\n", | 1259 | " | builtins.object\n", |
| 1195 | " | \n", | 1260 | " |\n", |
| 1196 | " | Data descriptors inherited from Adam:\n", | 1261 | " | Data descriptors inherited from Adam:\n", |
| 1197 | " | \n", | 1262 | " |\n", |
| 1198 | " | __dict__\n", | 1263 | " | __dict__\n", |
| 1199 | " | dictionary for instance variables (if defined)\n", | 1264 | " | dictionary for instance variables\n", |
| 1200 | " | \n", | 1265 | " |\n", |
| 1201 | " | __weakref__\n", | 1266 | " | __weakref__\n", |
| 1202 | " | list of weak references to the object (if defined)\n", | 1267 | " | list of weak references to the object\n", |
| 1203 | "\n" | 1268 | "\n" |
| 1204 | ] | 1269 | ] |
| 1205 | } | 1270 | } |
| @@ -1256,7 +1321,7 @@ | |||
| 1256 | }, | 1321 | }, |
| 1257 | { | 1322 | { |
| 1258 | "cell_type": "code", | 1323 | "cell_type": "code", |
| 1259 | "execution_count": 12, | 1324 | "execution_count": 52, |
| 1260 | "id": "95a1d0cc-0006-4ddc-b08d-d0b8b02acfba", | 1325 | "id": "95a1d0cc-0006-4ddc-b08d-d0b8b02acfba", |
| 1261 | "metadata": {}, | 1326 | "metadata": {}, |
| 1262 | "outputs": [ | 1327 | "outputs": [ |
| @@ -1324,7 +1389,7 @@ | |||
| 1324 | }, | 1389 | }, |
| 1325 | { | 1390 | { |
| 1326 | "cell_type": "code", | 1391 | "cell_type": "code", |
| 1327 | "execution_count": 13, | 1392 | "execution_count": 53, |
| 1328 | "id": "38066107-0190-4383-ba14-a4e9cd454a9c", | 1393 | "id": "38066107-0190-4383-ba14-a4e9cd454a9c", |
| 1329 | "metadata": {}, | 1394 | "metadata": {}, |
| 1330 | "outputs": [ | 1395 | "outputs": [ |
| @@ -1418,7 +1483,7 @@ | |||
| 1418 | "name": "python", | 1483 | "name": "python", |
| 1419 | "nbconvert_exporter": "python", | 1484 | "nbconvert_exporter": "python", |
| 1420 | "pygments_lexer": "ipython3", | 1485 | "pygments_lexer": "ipython3", |
| 1421 | "version": "3.11.7" | 1486 | "version": "3.13.9" |
| 1422 | } | 1487 | } |
| 1423 | }, | 1488 | }, |
| 1424 | "nbformat": 4, | 1489 | "nbformat": 4, |
