From be44243136d710eec0345f8459a64da377bab357 Mon Sep 17 00:00:00 2001 From: Simeon Simeonov Date: Sun, 29 Mar 2026 13:43:38 +0200 Subject: Re-format reveal.js slides and add templates --- notebooks/python/cryptographic_primitives.ipynb | 261 ++++++++++++++++++++++++ notebooks/python/python_3_8_to_3_11.ipynb | 2 +- notebooks/python/python_intro.ipynb | 120 +++++++---- notebooks/python/python_oo.ipynb | 151 ++++++++++---- 4 files changed, 451 insertions(+), 83 deletions(-) create mode 100644 notebooks/python/cryptographic_primitives.ipynb (limited to 'notebooks/python') 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 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 12, + "id": "fedd7d1a-36a9-4255-8305-a720dfb7b500", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "b\"{\\x8ea\\xf8wu!\\xeb\\xc4\\xc9\\xcb|\\\\\\xf1K\\xd5t\\x80i\\xbcD\\xb6!\\xfc\\xee\\x06lQ'V0\\xc6\"\n", + "7b8e61f8777521ebc4c9cb7c5cf14bd5748069bc44b621fcee066c51275630c6\n", + "b'e45h+Hd1IevEyct8XPFL1XSAabxEtiH87gZsUSdWMMY='\n" + ] + } + ], + "source": [ + "# hashes\n", + "\n", + "import base64\n", + "import hashlib\n", + "\n", + "hashed_result = hashlib.sha256(b\"This is a tesu\")\n", + "print(hashed_result.digest())\n", + "print(hashed_result.hexdigest())\n", + "print(base64.b64encode(hashed_result.digest()))\n" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "9082c388-1c30-46dc-8897-15bf5db255c8", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "password_hash1 = 'pbkdf2_sha256$100000$j2jYEb1TvX/wh5JEAYGag6h+hRNTGe3rjyv5pegVBu4=$7cV7MljnJJYuGndge3jozwFP28aP8Gfj+QFGvv5bpfo='\n", + "password_hash2 = 'pbkdf2_sha256$100000$8+PsThJmJXNAbQ/zaR5AgRA/CuNv8L1uPUXRNuWHc9k=$Kf07CSCVQOTQrvWX5l/ZBdA7zsaEFH247QjUaewpxaU='\n" + ] + } + ], + "source": [ + "# PBKDF2\n", + "\n", + "import base64\n", + "import hashlib\n", + "import os\n", + "\n", + "\n", + "def get_password_hash(plaintext_password: str) -> str:\n", + " \"\"\"\n", + " Returns a complete password hash based on `plaintext_password`\n", + "\n", + " :return: The hashed version of `plaintext_password`\n", + " :rtype: str\n", + " \"\"\"\n", + " # parameters typycally sent as arguments or fetched from config\n", + " hash_algo = \"sha256\" # the hash algorithm to use for HMAC\n", + " iterations = 100000 # amount of iterations\n", + "\n", + " salt = os.urandom(32) # 32 bytes of random data\n", + "\n", + " key = hashlib.pbkdf2_hmac(hash_algo, plaintext_password.encode(), salt, iterations)\n", + "\n", + " return (\n", + " f\"pbkdf2_{hash_algo}${iterations}$\"\n", + " f\"{base64.b64encode(salt).decode(\"ascii\")}$\"\n", + " f\"{base64.b64encode(key).decode(\"ascii\")}\"\n", + " )\n", + "\n", + "\n", + "def password_matches(plaintext_password: str, password_hash: str) -> bool:\n", + " \"\"\"Returns True if `plaintext_password` matches `password_hash`, False otherwise\"\"\"\n", + " hash_tokens = password_hash.split(\"$\")\n", + " hash_algo = hash_tokens[0].split(\"_\")[1]\n", + " iterations = int(hash_tokens[1])\n", + " salt = base64.b64decode(hash_tokens[2])\n", + " key = hashlib.pbkdf2_hmac(hash_algo, plaintext_password.encode(), salt, iterations)\n", + " return key == base64.b64decode(hash_tokens[3])\n", + "\n", + "\n", + "password_hash1 = get_password_hash(\"didn't expect a Spanish Inquisition!\")\n", + "password_hash2 = get_password_hash(\"didn't expect a Spanish Inquisition!\")\n", + "print(f\"{password_hash1 = }\")\n", + "print(f\"{password_hash2 = }\")\n", + "\n", + "# print(password_matches(\"didn't expect a Spanish Inquisition!\", 'pbkdf2_sha256$100000$JfwrS72mFimypbwhJ/NCyds2dkXtknvfWM5AM3+Z5GQ=$k8YA8csSJZhuYW0um7utq3+lSX5pTAQPd9dukObWrPo='))" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "1274325e-f670-4af8-a0bc-2ff7f668f769", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "('gxk9W0XFgbzbjRWB7zCPjbWs3', '00000bf5db2c3c6484904bf5e8314aeb123f5594af1f283fb62e14c7d196e84c')\n" + ] + } + ], + "source": [ + "# Proof of work\n", + "\n", + "import hashlib\n", + "import random\n", + "import string\n", + "\n", + "VALID_CHARS = string.ascii_letters + string.digits # valid characters for response\n", + "\n", + "\n", + "def pow_solve_rnd(challenge: str, starting_chars: str = \"00000\", answer_size: int = 25) -> tuple:\n", + " \"\"\"\n", + " Solves a POW challenge for SHA-256 using `random.choice`\n", + "\n", + " :return: (response, SHA-256 digest) tuple that solves the challenge\n", + " :rtype: tuple\n", + " \"\"\"\n", + " while True:\n", + " attempt = ''.join(\n", + " [random.choice(VALID_CHARS) for x in range(answer_size)])\n", + " dig = hashlib.sha256(('{0}:{1}'.format(challenge, attempt)).encode()).hexdigest()\n", + " if dig.startswith(starting_chars):\n", + " return attempt, dig\n", + "\n", + "print(f\"{pow_solve_rnd('2024-08-07')}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "6752c055-7e49-46c4-858b-599d069a7e77", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "enc-val$2$6ppVDmiJi0P9BnHuzwFUPA+vV/Gsb4INe2O9E/Ma2uI=$6879D8rw8645nbQ/NQtY3Q+AwKTWpd1P6Q==\n", + "Invalid tag when decrypting enc-val$2$tmuH3MTDQ9vMbxJ1pZUb0rQlF43Lgr/AYfkA+vSOZs0=$5CVt3VrxhuuOOVt1vCXeGpbhNh/20IcFNg==\n", + "None\n" + ] + } + ], + "source": [ + "# Encryption / decryption using AES-256-GCM\n", + "\n", + "import hashlib\n", + "import os\n", + "\n", + "from cryptography.exceptions import InvalidTag\n", + "from cryptography.hazmat.primitives.ciphers.aead import AESGCM\n", + "\n", + "\n", + "def encrypt(password: str, data: str) -> str:\n", + " \"\"\"\n", + " Encrypts `data` using `password` and AES-256-GCM.\n", + "\n", + " The output string is in the following format:\n", + " enc-val$`version-num`$`bas64-salt`$`base64-encrypted_data`\n", + "\n", + " :param password: The password to generate the key with\n", + " :type password: str\n", + "\n", + " :param data: The data to be encrypted\n", + " :type data: str\n", + "\n", + " :return: The output string\n", + " :rtype: str\n", + " \"\"\"\n", + " data_bytes = data.encode()\n", + " salt = os.urandom(32)\n", + " aesgcm = AESGCM(\n", + " hashlib.scrypt(\n", + " password.encode(), salt=salt, n=2**14, r=8, p=1, dklen=32\n", + " )\n", + " )\n", + " nonce = salt[:12]\n", + " # padding_length_bytes = b\"-1\" # no padding used 2 bytes \"sign\"\n", + " edata = aesgcm.encrypt(\n", + " nonce, data_bytes, salt\n", + " )\n", + " return (\n", + " f\"enc-val$2${base64.b64encode(salt).decode()}${base64.b64encode(edata).decode()}\"\n", + " )\n", + "\n", + "\n", + "def decrypt(password: str, edata: str) -> str:\n", + " \"\"\"\n", + " Decrypts `edata` using `password`.\n", + "\n", + " `edata` is in the following format:\n", + " enc-val$`version-num`$`bas64-salt`$`base64-encrypted_data`\n", + "\n", + " :param password: The password to generate the key with\n", + " :type password: str\n", + "\n", + " :param edata: The data to be decrypted\n", + " :type edata: str\n", + "\n", + " :return: The output string / decrypted data\n", + " :rtype: str\n", + " \"\"\"\n", + " # check for supported versions first...\n", + "\n", + " try:\n", + " salt, data = (base64.b64decode(t) for t in edata[10:].split('$'))\n", + " nonce = salt[:12]\n", + " aesgcm = AESGCM(\n", + " hashlib.scrypt(\n", + " password.encode(), salt=salt, n=2**14, r=8, p=1, dklen=32\n", + " )\n", + " )\n", + "\n", + " # decrypt\n", + " data = aesgcm.decrypt(nonce, data, salt)\n", + " return data.decode()\n", + " except InvalidTag:\n", + " print(f\"Invalid tag when decrypting {edata}\")\n", + "\n", + "print(f\"{encrypt('my passphrase', 'my secret')}\")\n", + "print(f\"{decrypt('my passphrase', 'enc-val$2$tmuH3MTDQ9vMbxJ1pZUb0rQlF43Lgr/AYfkA+vSOZs0=$5CVt3VrxhuuOOVt1vCXeGpbhNh/20IcFNg==')}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8ccfb453-d3fd-4383-a01d-f81b19f2cb82", + "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.13.9" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} 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 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.7" + "version": "3.12.3" } }, "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 @@ -48,6 +48,19 @@ "- Calling C from Python: Cython, CFFI, ctypes" ] }, + { + "cell_type": "markdown", + "id": "779bac39-4646-4b80-9970-343a3daef1e7", + "metadata": {}, + "source": [ + "## Important PEPs\n", + "\n", + "- [PEP0](https://peps.python.org/) - Index of Python Enhancement Proposals\n", + "- [PEP8](https://peps.python.org/pep-0008/) - Style Guide for Python Code\n", + "- [PEP257](https://peps.python.org/pep-0257/) - Docstring Conventions\n", + "- [PEP484](https://peps.python.org/pep-0484/) - Type Hints" + ] + }, { "cell_type": "markdown", "id": "6d1edcfc-b001-4393-9c24-72adc9b5fccf", @@ -72,7 +85,7 @@ }, { "cell_type": "markdown", - "id": "4f144f73-51c1-419d-b71e-ee513f68f41c", + "id": "81573ed5-b76e-4726-9f44-7dc017fd0896", "metadata": {}, "source": [ "## Built-in functions\n", @@ -81,8 +94,6 @@ "\n", "[https://docs.python.org/3/library/functions.html](https://docs.python.org/3/library/functions.html)\n", "\n", - "- `dir([obj])` - returns a list of valid attributes for that object\n", - "\n", "- `id(obj)` - returns the \"identity\" of an object - an integer which is guaranteed to be unique\n", "\n", "- `print(...)` - prints objects to a text stream\n", @@ -92,6 +103,12 @@ "- `type(obj)` - returns the type of an object" ] }, + { + "cell_type": "markdown", + "id": "bc180243-fe11-405c-beb9-83db4f793a05", + "metadata": {}, + "source": [] + }, { "cell_type": "markdown", "id": "a0c57535-e285-47af-80f9-1d4a16de5f25", @@ -116,26 +133,28 @@ "source": [ "# Common built-in types:\n", "\n", - "s = 'foo' # this is a string / str, same as str('foo'), may be encoded, immutable (s[0] = 'r' is NOT possible)\n", + "my_string = 'foo' # a string / str, same as str('foo'), may be encoded\n", + " # immutable (s[0] = 'r' is NOT possible)\n", "\n", - "b = b'foo' # bytes, same as bytes('foo', 'utf-8'), may be decoded, immutable\n", + "my_bytes = b'foo' # bytes, same as bytes('foo', 'utf-8'), may be decoded, immutable\n", "\n", - "i = 6 # int, same as int('6'), immutable\n", + "my_int = 6 # int, same as int('6'), immutable\n", "\n", - "f = 0.1 # float, same as float('0.1'), immutable, Note!!: Floats have a fixed size,\n", - "# hence they don't necessarily behave they way we expect from math class.\n", + "my_float = 0.1 # float, same as float('0.1'), immutable, Note!!: Floats have a fixed size,\n", + " # hence they don't necessarily behave they way we expect from math class.\n", + "# 24732847234232342892343428.3 == 24732847234232342892343428.1 # True\n", "\n", - "b = False # bool, same as bool(0), bool(''), bool(None)... immutable / constant\n", + "my_bool = False # bool, same as bool(0), bool(''), bool(None)... immutable / constant\n", "\n", - "n = None # NoneType, similar to 'null' in other languages, immutable / constant\n", + "my_none = None # NoneType, similar to 'null' in other languages, immutable / constant\n", "\n", - "l = [1, False, 'foo'] # list, same as list((1, False, 'foo'))\n", + "my_list = [1, False, 'foo'] # list, same as list((1, False, 'foo'))\n", "\n", - "t = (1, False, 'foo') # tuple, same as tuple([1, False, 'foo']), immutable\n", + "my_tuple = (1, False, 'foo') # tuple, same as tuple([1, False, 'foo']), immutable\n", "\n", - "d = {'foo': 1, 'bar': 8} # dict, same as dict(foo=1, bar=8), similar to hash in other languages\n", + "my_dict = {'foo': 1, 'bar': 8} # dict, same as dict(foo=1, bar=8), similar to hash in other languages\n", "\n", - "s = {'foo', 'bar', 1, 1, 4} # set, same as set(['foo', 'bar', 1, 1, 4]), removes duplicates" + "my_set = {'foo', 'bar', 1, 1, 4} # set, same as set(['foo', 'bar', 1, 1, 4]), removes duplicates" ] }, { @@ -203,7 +222,7 @@ "s3[0] # Out: S\n", "s3[-1] # Out: t\n", "s3[1:] # Out: tatnett\n", - "s3[1:-1] # Out: tatnett\n", + "s3[1:-1] # Out: tatnet\n", "s3[-3:] # Out: ett\n", "s3[1:-1:2] # Out: tte\n", "\n", @@ -221,6 +240,29 @@ "b'B\\xc3\\x98!'.decode('utf-8') # utf-8 is implicit in Python 3" ] }, + { + "cell_type": "markdown", + "id": "25d0f9dc-d783-4a23-a3ee-c0b6cee3d4ab", + "metadata": {}, + "source": [ + "## Lists vs. tuples\n", + "\n", + "Tuples are not sumply \"read-only\" lists.\n", + "\n", + "\"Though tuples may seem similar to lists, they are often used in different situations and for different purposes.\n", + "Tuples are immutable, and usually contain a heterogeneous sequence of elements that are accessed via unpacking ... or indexing.\n", + "Lists are mutable, and their elements are usually homogeneous and are accessed by iterating over the list.\n", + "\n", + "```python\n", + "my_tuple = (1,2)\n", + "my_list = [1,2] \n", + "\n", + "# tuples are immutable and hashable, hence they can be used as keys in mapping objects as dicts\n", + "d = {my_tuple: 1} # OK\n", + "d = {my_list: 1} # Error\n", + "```" + ] + }, { "cell_type": "markdown", "id": "6b2bc8fa-a5dc-4a58-92b2-abfb19bfe2ed", @@ -302,7 +344,7 @@ "id": "40db50e4-fb7a-4a77-a074-bc4413f27212", "metadata": {}, "source": [ - "## Creating and maintaining a Python environment (cont...)\n", + "## Creating and maintaining a Python environment - goals\n", "\n", "Desired qualities for a flexible Python environment:\n", "\n", @@ -324,10 +366,11 @@ "id": "c661bf56-add0-402c-a629-a857f81d0758", "metadata": {}, "source": [ - "## Creating and maintaining a Python environment (cont...)\n", + "## Creating and maintaining a Python environment - using devbox / WSL / UNIX systems directly\n", "\n", "Exploting the operating system can be done by:\n", "\n", + "- 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", "- (re)defining `PYTHONPATH`\n", "- using symlinks to point at packages placed at different locations" ] @@ -337,7 +380,7 @@ "id": "db8952aa-0944-4644-9626-7460786fd72a", "metadata": {}, "source": [ - "## Creating and maintaining a Python environment (cont...)\n", + "## Creating and maintaining a Python environment - using the Python virtual environment - *venv*\n", "\n", "Using venv can be done by directly invoking python:\n", "\n", @@ -365,10 +408,13 @@ "id": "299641e6-3d87-4a86-a124-eb49edb75b4b", "metadata": {}, "source": [ - "## Creating and maintaining a Python environment (cont...)\n", + "## Creating and maintaining a Python environment - using Poetry\n", "\n", "Poetry [https://python-poetry.org](https://python-poetry.org) is the prefered environment and dependency management tool at Statnett.\n", "\n", + "Although Poetry creates and uses vierual environment(s) behind the scenes,\n", + "it is centered around the concept of \"projects\" and not the virtual environment itself\n", + "\n", "```bash\n", "# create project and a virtual environment from scratch\n", "poetry new my-project\n", @@ -411,7 +457,7 @@ { "data": { "text/plain": [ - "-42990858669078524" + "-3158428850515418558" ] }, "execution_count": 15, @@ -435,8 +481,8 @@ "hash(t) # returns f.i. -6333845781340707986\n", "# hash(l) # TypeError: unhashable type: 'list'\n", "\n", - "s = 'the long and winding road'\n", - "s2 = 'the long and winding road'\n", + "s = \"the long and winding road\"\n", + "s2 = \"the long and winding road\"\n", "\n", "# check if s and s2 are the same object:\n", "id(s) # Out: 139858905258704\n", @@ -816,7 +862,7 @@ "# the function will behave differently depending on what parameters were\n", "# used when calling its enclosing function (factory function)\n", "\n", - "def get_multiplier_of(base: int) -> str:\n", + "def get_multiplier_of(base: int):\n", " \"\"\"the function enclosing its nested functions\"\"\"\n", " # this function is the enclosing function of the function `multiplier_function`\n", "\n", @@ -958,19 +1004,7 @@ "execution_count": 22, "id": "4346260d-76af-437e-9c10-1c1d0c478695", "metadata": {}, - "outputs": [ - { - "ename": "NameError", - "evalue": "name 'is_admin' is not defined", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", - "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", - "\u001b[0;31mNameError\u001b[0m: name 'is_admin' is not defined" - ] - } - ], + "outputs": [], "source": [ "# Decorators (cont ...) - a complete example\n", "\n", @@ -996,7 +1030,7 @@ "\n", "\n", "@requires_access(access_secret='b28cfeaa65b73cf')\n", - "@is_admin\n", + "# @is_admin - decorators can be \"chained\"\n", "def sensitive_function(data, **kwargs):\n", " \"\"\"very sensitive function\"\"\"\n", " db.save(data)" @@ -1030,11 +1064,19 @@ "\n", "\n", "# modern Python >= 3.6 f-strings\n", - "f'{s} - {i} - {f:5.2f}' # Out: 'another string - 27 - 6.58'\n", + "f\"{s} - {i} - {f:5.2f}\" # Out: 'another string - 27 - 6.58'\n", "``` \n", "\n", "See https://docs.python.org/3/library/string.html#formatspec for the complete format specification" ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "23972592-e838-4fa7-81f1-ca3ca129e757", + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { @@ -1053,7 +1095,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.7" + "version": "3.13.9" } }, "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 @@ "\n", "- improving modularity\n", "\n", - "- providing foundation for a more intuitive design" + "- providing foundation for a more intuitive design\n", + "\n", + "A pseudo code example:\n", + "\n", + "```c\n", + "/* crating and drawing a (pseudo) widget in C */\n", + "my_widget = mylibrary_widget_init(); /* a struct of the type widget */\n", + "mylibrary_widget_draw(my_widget);\n", + "```\n", + "\n", + "```python\n", + "# creating and drawing a (pseudo) widget in Python\n", + "import mylibrary\n", + "\n", + "my_widget = mylibrary.Widget()\n", + "my_widget.draw()\n", + "```" + ] + }, + { + "cell_type": "markdown", + "id": "878cb805-a2d7-46a3-a34a-0507495420f7", + "metadata": {}, + "source": [ + "# Do I need to know / understand OOP when I am programming in Python?\n", + "\n", + "Most definitely - **yes**.\n", + "\n", + "While using programming language that is not object-oriented (like *C* and / or *Rust*) is always possible,\n", + "when using Python there are no *sane* alternatives that have ever been demonstrated:\n", + "\n", + "- Understanding OO behaviour and syntax is a must in order to understand Python code in general\n", + "- OOP is at the core of Python's design - everything in Python is an object\n", + "- 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", + "- Following the priciples described above is more challenging when not using OO in general." + ] + }, + { + "cell_type": "markdown", + "id": "b83dc815-ed51-4e5a-a30e-fa0252217194", + "metadata": {}, + "source": [ + "# I am doing only simple things with Python. Do I still need to learn OOP?\n", + "\n", + "Most definitely - **yes**.\n", + "\n", + "- A simple task provides a great opportunity to learn and practice OOP\n", + "- Using OOP **does not mean overcomplicating** your code. When applied correctly - it means the opposite\n", + "- Doing \"simple things\" exclusively is not very ambitious approach to programming and learning in general" ] }, { @@ -109,13 +157,20 @@ "\n", "- object - an instance of a class that may contain its own attributes as well as references to its class' attributes\n", "\n", - "- attribute - variable, property, function defined in the class and present in its instances\n", + "- 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", "\n", "- class variable - attribute of which a single copy exists, regardless of how many instances of the class exist\n", "\n", "- object / instance variable, object / instance attribute - attribute for which each instantiated object of the class has a separate copy, or instance\n", "\n", - "- method - member function - function that is an attribute" + "- method - member function - function that is an attribute\n", + "\n", + "```python\n", + "# my_str_obj is an object (instance) of the class str\n", + "my_str_object = str(\"Test\") # or my_str_object = \"Test\"\n", + "my_str_object.lower # attribute that is a function -> method\n", + "my_str_object.lower() # calling the method \"lower\"\n", + "```" ] }, { @@ -218,7 +273,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 41, "id": "a9c30a5d-aa86-45ab-b897-f051df80f1d8", "metadata": {}, "outputs": [ @@ -226,17 +281,17 @@ "name": "stdout", "output_type": "stream", "text": [ - "car1.get_obj_info_str() = \"I am <__main__.Car object at 0x7f70e643d590> with id 140122876269968 from with id 93964809433504\"\n", - "car2.get_obj_info_str() = \"I am <__main__.Car object at 0x7f70e63ddf10> with id 140122875879184 from with id 93964809433504\"\n", - "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", - "car2.model = 'Scoda', car2.reg_nr = 'BD77655', car2.extras = ['GPSnav'], id(car2.cls_extras) = 140122875882432, id(car2.get_obj_info_str) = 140122875880192\n", - "id(Car.cls_extras) = 140122875882432, id(Car.get_obj_info_str) = 140122875964416\n", - "car1.cls_extras = ['GPSnav', 'Sound system'], id(car1.cls_extras) = 140122875882432\n", - "car2.cls_extras = ['GPSnav', 'Sound system'], id(car2.cls_extras) = 140122875882432\n", + "car1.get_obj_info_str() = \"I am <__main__.Car object at 0x7fd00029ff80> with id 140531332677504 from with id 93887620597168\"\n", + "car2.get_obj_info_str() = \"I am <__main__.Car object at 0x7fd00029ea50> with id 140531332672080 from with id 93887620597168\"\n", + "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", + "car2.model = 'Scoda', car2.reg_nr = 'BD77655', car2.extras = ['GPSnav'], id(car2.cls_extras) = 140531337557440, id(car2.get_obj_info_str) = 140531251728064\n", + "id(Car.cls_extras) = 140531337557440, id(Car.get_obj_info_str) = 140531252010400\n", + "car1.cls_extras = ['GPSnav', 'Sound system'], id(car1.cls_extras) = 140531337557440\n", + "car2.cls_extras = ['GPSnav', 'Sound system'], id(car2.cls_extras) = 140531337557440\n", "True\n", "hasattr(car1, 'import_tax_paid') = True\n", "hasattr(car2, 'import_tax_paid') = False\n", - "id(car1.__class__) = 93964809433504, id(car2.__class__) = 93964809433504, id(Car) = 93964809433504\n" + "id(car1.__class__) = 93887620597168, id(car2.__class__) = 93887620597168, id(Car) = 93887620597168\n" ] } ], @@ -288,7 +343,7 @@ "# - checks if 'attr' is an instance attribute\n", "# - checks if 'attr' is a class attribute (through the method resolution order - MRO)\n", "# - raises AttributeError\n", - "# setter:\n", + "# setter (making the attribute writable):\n", "# - (re)defines an instance attribute\n", "\n", "# Details not covered in this course:\n", @@ -323,7 +378,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 42, "id": "391e816e-c53b-454b-9e9f-3587234f1fea", "metadata": {}, "outputs": [ @@ -413,7 +468,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 43, "id": "e82feefb-025d-4cbb-a8ac-ddc3cc01269c", "metadata": {}, "outputs": [ @@ -495,7 +550,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 44, "id": "71a90ec6-3cc0-441e-a866-c2c2b9984559", "metadata": {}, "outputs": [ @@ -613,7 +668,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 45, "id": "a4d2735d-d167-4f43-ad55-cb3d13ece2dc", "metadata": {}, "outputs": [ @@ -656,7 +711,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 46, "id": "a6ead5e6-e987-4bda-bf48-30f91877278d", "metadata": {}, "outputs": [ @@ -715,7 +770,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 47, "id": "11f6a3fb-64d7-40a9-a130-27548ec4b802", "metadata": {}, "outputs": [ @@ -797,7 +852,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 48, "id": "c345a69f-02da-40e7-bb37-c0511b6af096", "metadata": {}, "outputs": [ @@ -806,6 +861,9 @@ "output_type": "stream", "text": [ "9\n", + "9\n", + "140531251904896\n", + "140531251904896\n", "Vector.from_str('1:1:5:6') = Vector(start=Point(x=1, y=1), end=Point(x=5, y=6))\n" ] } @@ -837,7 +895,14 @@ " Point(coordinates[2], coordinates[3]),\n", " )\n", "\n", - "print(Point.get_manhattan_distance(Point(1, 1), Point(5, 6)))\n", + "point1 = Point(1, 1)\n", + "point2 = Point(5, 6)\n", + "print(f\"{point1.get_manhattan_distance(point1, point2)}\") # works, but should not be used in that way\n", + "print(f\"{Point.get_manhattan_distance(point1, point2)}\")\n", + "print(f\"{id(point1.get_manhattan_distance)}\")\n", + "print(f\"{id(point2.get_manhattan_distance)}\")\n", + "# print(f\"{id(point1.from_str)}\") <-- not available as an instance attribute\n", + "\n", "print(f\"{Vector.from_str('1:1:5:6') = }\")" ] }, @@ -890,7 +955,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 49, "id": "44d47e1c-1a06-4577-8db6-6ec78ce5cef8", "metadata": {}, "outputs": [ @@ -903,32 +968,32 @@ "\n", "class TigerShark(Fish)\n", " | TigerShark(weight: int, alive: bool = True, **kwargs)\n", - " | \n", + " |\n", " | Base class for all tiger sharks\n", - " | \n", + " |\n", " | Method resolution order:\n", " | TigerShark\n", " | Fish\n", " | Animal\n", " | builtins.object\n", - " | \n", + " |\n", " | Methods defined here:\n", - " | \n", + " |\n", " | __init__(self, weight: int, alive: bool = True, **kwargs)\n", " | Initialize self. See help(type(self)) for accurate signature.\n", - " | \n", + " |\n", " | ----------------------------------------------------------------------\n", " | Data descriptors inherited from Animal:\n", - " | \n", + " |\n", " | __dict__\n", - " | dictionary for instance variables (if defined)\n", - " | \n", + " | dictionary for instance variables\n", + " |\n", " | __weakref__\n", - " | list of weak references to the object (if defined)\n", - " | \n", + " | list of weak references to the object\n", + " |\n", " | alive\n", " | getter property alive\n", - " | \n", + " |\n", " | weight\n", " | getter property weight\n", "\n", @@ -1064,7 +1129,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 50, "id": "e6076925-75f0-456c-aed5-7db0a24a67a1", "metadata": {}, "outputs": [ @@ -1169,7 +1234,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 51, "id": "cf79617a-95d2-48f2-be66-fa1fa34e4e04", "metadata": {}, "outputs": [ @@ -1192,14 +1257,14 @@ " | Adam\n", " | Eve\n", " | builtins.object\n", - " | \n", + " |\n", " | Data descriptors inherited from Adam:\n", - " | \n", + " |\n", " | __dict__\n", - " | dictionary for instance variables (if defined)\n", - " | \n", + " | dictionary for instance variables\n", + " |\n", " | __weakref__\n", - " | list of weak references to the object (if defined)\n", + " | list of weak references to the object\n", "\n" ] } @@ -1256,7 +1321,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 52, "id": "95a1d0cc-0006-4ddc-b08d-d0b8b02acfba", "metadata": {}, "outputs": [ @@ -1324,7 +1389,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 53, "id": "38066107-0190-4383-ba14-a4e9cd454a9c", "metadata": {}, "outputs": [ @@ -1418,7 +1483,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.7" + "version": "3.13.9" } }, "nbformat": 4, -- cgit v1.3