From 28d1350e41c56f7d86a70f1b662cfb455e55a630 Mon Sep 17 00:00:00 2001 From: Nick Murphy Date: Wed, 24 Jul 2024 14:50:52 -0400 Subject: [PATCH] Add content on type hints and decorators --- ...asmapy-particles-formulary-completed.ipynb | 364 +++++++++++++++--- 1 file changed, 302 insertions(+), 62 deletions(-) diff --git a/notebooks/plasmapy-particles-formulary-completed.ipynb b/notebooks/plasmapy-particles-formulary-completed.ipynb index 16e37d3..4b8401e 100644 --- a/notebooks/plasmapy-particles-formulary-completed.ipynb +++ b/notebooks/plasmapy-particles-formulary-completed.ipynb @@ -1268,7 +1268,7 @@ "id": "44e501d8-3065-4dea-a673-7b953725902d", "metadata": {}, "source": [ - "### Type hint annotations" + "### Prelude: type hint annotations" ] }, { @@ -1298,7 +1298,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 179, "id": "cd171e0c-7233-479d-9e3d-18d5b71e7c3e", "metadata": {}, "outputs": [], @@ -1318,7 +1318,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 180, "id": "8733b1c7-1d85-463a-9d63-718379032fb0", "metadata": {}, "outputs": [], @@ -1338,7 +1338,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 181, "id": "24b7a377-0370-48ba-a3f6-a64181508a11", "metadata": {}, "outputs": [], @@ -1356,7 +1356,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 182, "id": "1c74d5de-3a75-4ea8-bac9-ac5dcb94f9e6", "metadata": {}, "outputs": [], @@ -1378,7 +1378,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 183, "id": "9190e68f-88d0-4746-93e0-5527f7ea014f", "metadata": {}, "outputs": [], @@ -1397,7 +1397,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 184, "id": "3607d300-7ede-4a6c-a4bd-464877c2ea6a", "metadata": {}, "outputs": [], @@ -1408,7 +1408,13 @@ }, { "cell_type": "markdown", - "id": "0eebd249-b571-4dfd-b94d-37a61d58a481", + "id": "3dd76396-9b2d-4389-bd57-2263bbc17b13", + "metadata": {}, + "source": [] + }, + { + "cell_type": "markdown", + "id": "28fa6bed-52ad-4a2c-b51c-32e2ba7e5c49", "metadata": {}, "source": [ "### Decorators" @@ -1416,77 +1422,256 @@ }, { "cell_type": "markdown", - "id": "07ddb79b-8993-4737-a996-cb228a464c59", + "id": "ce91e852-6f12-434d-943b-5ca64225b550", "metadata": {}, "source": [ - "Functions in Python are objects. For example, we can write a function that returns a function:" + "In Python, functions are objects. This means that we can write a function that returns another function." ] }, { "cell_type": "code", - "execution_count": null, - "id": "2509ca65-99d8-40eb-b9ea-2a2f094ae5cf", + "execution_count": 125, + "id": "e8d7e33f-9dd8-4351-a498-cd4ced4ce68c", "metadata": {}, "outputs": [], "source": [ "from typing import Callable\n", "\n", - "def return_max_or_min(get_biggest: bool) -> Callable:\n", - " return max if get_biggest else min" + "def return_function() -> Callable:\n", + "\n", + " print(\"Defining inner_function!\")\n", + " \n", + " def inner_function(): \n", + " print(\"Calling inner_function!\")\n", + " \n", + " return inner_function" ] }, { "cell_type": "code", - "execution_count": null, - "id": "46d8903d-0c7b-4b69-8080-a7c61706a60a", + "execution_count": 126, + "id": "ef09dad1-3d99-4d36-ae81-795433d25711", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Defining inner_function!\n" + ] + } + ], + "source": [ + "function = return_function()" + ] + }, + { + "cell_type": "code", + "execution_count": 127, + "id": "ac56c46b-06d5-4296-b9c6-40697f25be11", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Calling inner_function!\n" + ] + } + ], + "source": [ + "function()" + ] + }, + { + "cell_type": "markdown", + "id": "03d3bae8-e510-4bcf-a01d-4d998e0b24a5", + "metadata": {}, + "source": [ + "Or we can pass a function as an argument to another function!" + ] + }, + { + "cell_type": "code", + "execution_count": 128, + "id": "db188ae4-5ff7-48ff-b335-6d0bbfbe8b2f", + "metadata": {}, + "outputs": [], + "source": [ + "def apply_function_to_array(\n", + " function: Callable, \n", + " array: list[int],\n", + ") -> int:\n", + " \n", + " return function(array)" + ] + }, + { + "cell_type": "code", + "execution_count": 129, + "id": "3cc6dc5c-37f8-45a4-813c-5576b2e326f7", "metadata": {}, "outputs": [], "source": [ - "array = [1, 2, 3]\n", + "array = [1, 2, 3, 4, 5]" + ] + }, + { + "cell_type": "code", + "execution_count": 130, + "id": "2137ef7e-f0ac-4c7a-8a8b-596c98e2402d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "5" + ] + }, + "execution_count": 130, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "apply_function_to_array(max, array)" + ] + }, + { + "cell_type": "markdown", + "id": "bf4b7d59-1f13-42e6-87df-b3a31d635b4f", + "metadata": {}, + "source": [ + "[**decorator**]: https://www.geeksforgeeks.org/decorators-in-python/\n", "\n", - "function = max_or_min(get_biggest=True)\n", + "In Python, a function that _modifies another function_ is called a [**decorator**]. Let's try one out that \n", "\n", - "function(array)" + "" ] }, { "cell_type": "code", - "execution_count": null, - "id": "fd1b8454-cf5d-4d6f-807f-b7f87db00381", + "execution_count": 131, + "id": "5095d1d9-a6de-401d-994c-83ffd16271c1", "metadata": {}, "outputs": [], - "source": [] + "source": [ + "def decorator(function: Callable):\n", + " \n", + " def decorated_function() -> None:\n", + " \n", + " print(\"Before calling the function.\")\n", + " result = function()\n", + " print(\"After calling the function.\")\n", + " \n", + " return result\n", + " \n", + " return decorated_function" + ] }, { - "cell_type": "markdown", - "id": "3dd76396-9b2d-4389-bd57-2263bbc17b13", + "cell_type": "code", + "execution_count": 138, + "id": "537d6027-fefc-4117-8c37-44555d38c49c", "metadata": {}, + "outputs": [], + "source": [ + "def example_function() -> None:\n", + " print(\"Inside original example_function!\")" + ] + }, + { + "cell_type": "code", + "execution_count": 139, + "id": "a5c89a55-260b-46de-bb30-6023e3f90c2c", + "metadata": {}, + "outputs": [], + "source": [ + "example_function = decorator(example_function)" + ] + }, + { + "cell_type": "code", + "execution_count": 141, + "id": "d15b8474-7cc1-4e0f-83cc-2f56d2c387ec", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Before calling the function.\n", + "Inside original example_function!\n", + "After calling the function.\n" + ] + } + ], "source": [ - "Decorators in Python are a way to modify or enhance functions without changing their actual code. They wrap another function, adding extra functionality before and after the original function runs. You use decorators by prefixing a function definition with the decorator's name, preceded by an `@` symbol." + "example_function()" ] }, { "cell_type": "markdown", - "id": "fa1166ed-a06e-4eff-99a9-6288f110d37f", + "id": "41320c46-19d7-4cbc-9ad7-dda5933208de", "metadata": {}, "source": [ - "Let's look at a _recursive_ function that computes the $n$th Fibonacci number. We'll use `@lru_cache`, which caches the result from a function so that it doesn't need to be recalculated every time." + "In Python, we have special syntax for decorators" ] }, { "cell_type": "code", - "execution_count": 1, - "id": "dfa6e19a-3a56-4e96-805b-c7e856f0ba22", + "execution_count": 142, + "id": "2f08b76f-d26b-4124-b453-a04d210366ba", "metadata": {}, "outputs": [], "source": [ - "from functools import lru_cache" + "@decorator\n", + "def another_function() -> None:\n", + " print(\"Inside function2!\")" ] }, { "cell_type": "code", - "execution_count": 13, - "id": "963e540e-435a-4e02-a9ec-36712de58665", + "execution_count": 143, + "id": "b8f5eec3-136c-4ca8-876f-2a02362a32d1", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Before calling the function.\n", + "Inside function2!\n", + "After calling the function.\n" + ] + } + ], + "source": [ + "function2()" + ] + }, + { + "cell_type": "markdown", + "id": "4508ad5d-88a5-41e9-aba1-51d2bbe2907d", + "metadata": {}, + "source": [ + "#### Example: Fibonacci sequence with caching" + ] + }, + { + "cell_type": "markdown", + "id": "4bc96fbc-4b0c-4190-a1ca-9313bb149342", + "metadata": {}, + "source": [ + "Let's look at a _recursive_ function that computes the $n$th Fibonacci number. If we define $F_0 ≡ 0$ and $F_1 ≡ 1$, then $ F_n = F_{n-1} + F_{n-2}$.\n", + "\n", + "Here's an example recursive function:" + ] + }, + { + "cell_type": "code", + "execution_count": 147, + "id": "27b42156-f06e-484d-ae8c-767d8267fb4d", "metadata": {}, "outputs": [], "source": [ @@ -1497,62 +1682,44 @@ }, { "cell_type": "code", - "execution_count": 14, - "id": "9b8ce92f-1c1f-417d-8fc9-62cb2381803f", + "execution_count": 148, + "id": "e7ddee57-66fb-4c28-9cd9-30fddd7d0b0b", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Calculating Fibonacci number for n = 3\n", "Calculating Fibonacci number for n = 2\n", "Calculating Fibonacci number for n = 1\n", - "Calculating Fibonacci number for n = 0\n", - "Calculating Fibonacci number for n = 1\n" + "Calculating Fibonacci number for n = 0\n" ] }, { "data": { "text/plain": [ - "2" + "1" ] }, - "execution_count": 14, + "execution_count": 148, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "fibonacci(3)" + "fibonacci(2)" ] }, { "cell_type": "code", - "execution_count": 15, - "id": "2b749044-adc8-4321-952f-6dd7f61cd898", + "execution_count": 175, + "id": "fca36cc4-9349-4433-b13c-e2e2a1e66c23", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Calculating Fibonacci number for n = 6\n", - "Calculating Fibonacci number for n = 5\n", - "Calculating Fibonacci number for n = 4\n", - "Calculating Fibonacci number for n = 3\n", - "Calculating Fibonacci number for n = 2\n", - "Calculating Fibonacci number for n = 1\n", - "Calculating Fibonacci number for n = 0\n", - "Calculating Fibonacci number for n = 1\n", - "Calculating Fibonacci number for n = 2\n", - "Calculating Fibonacci number for n = 1\n", - "Calculating Fibonacci number for n = 0\n", - "Calculating Fibonacci number for n = 3\n", - "Calculating Fibonacci number for n = 2\n", - "Calculating Fibonacci number for n = 1\n", - "Calculating Fibonacci number for n = 0\n", - "Calculating Fibonacci number for n = 1\n", "Calculating Fibonacci number for n = 4\n", "Calculating Fibonacci number for n = 3\n", "Calculating Fibonacci number for n = 2\n", @@ -1567,25 +1734,98 @@ { "data": { "text/plain": [ - "8" + "3" + ] + }, + "execution_count": 175, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "fibonacci(4)" + ] + }, + { + "cell_type": "markdown", + "id": "bb81be84-72a8-428d-8324-c9690f350719", + "metadata": {}, + "source": [ + "We'll use `@functools.cache`, which _caches_ (stores) the result from a function so that it doesn't need to be recalculated every time. " + ] + }, + { + "cell_type": "code", + "execution_count": 176, + "id": "bb8147dc-165d-48e7-a5ae-a325410b2d22", + "metadata": {}, + "outputs": [], + "source": [ + "from functools import cache\n", + "\n", + "@cache\n", + "def fibonacci_cached(n: int) -> int:\n", + " print(\"Calculating Fibonacci number for n =\", n)\n", + " return n if n < 2 else fibonacci_cached(n - 1) + fibonacci_cached(n - 2)" + ] + }, + { + "cell_type": "code", + "execution_count": 177, + "id": "c861945a-c8e9-43d9-9dfe-00bde320129c", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "1" ] }, - "execution_count": 15, + "execution_count": 177, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "fibonacci(6)" + "fibonacci_decorated(2)" + ] + }, + { + "cell_type": "code", + "execution_count": 178, + "id": "01f5996f-5218-45c8-9fa1-c97755600a3e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "5" + ] + }, + "execution_count": 178, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "fibonacci_decorated(5)" ] }, { "cell_type": "code", "execution_count": null, - "id": "6fe2840e-e8e6-4a93-8aa8-e010674f14e4", + "id": "81673a7b-3cd1-4868-be01-f5fbbb9e0bbd", "metadata": {}, "outputs": [], "source": [] + }, + { + "cell_type": "markdown", + "id": "b8387a7f-a9b2-44c0-b1d9-d0b45d7ea44d", + "metadata": {}, + "source": [ + "fibonacci_decorated(5)" + ] } ], "metadata": {