From 77c7f673e5bba82493a605de32515d867de6f538 Mon Sep 17 00:00:00 2001 From: hifly81 Date: Sat, 1 Nov 2025 02:07:17 -0700 Subject: [PATCH] added info on advanced functions --- 11_decorator_closure.ipynb | 100 +++++++++++++++++++++++++++++++------ 1 file changed, 86 insertions(+), 14 deletions(-) diff --git a/11_decorator_closure.ipynb b/11_decorator_closure.ipynb index 1f84abc..d7f6569 100644 --- a/11_decorator_closure.ipynb +++ b/11_decorator_closure.ipynb @@ -21,10 +21,15 @@ "source": [ "## 🧩 Funzioni come oggetti di prima classe\n", "\n", - "In Python, le funzioni possono essere trattate come **oggetti**, cioè:\n", - "- possono essere passate come argomento,\n", - "- ritornate da altre funzioni,\n", - "- assegnate a variabili." + "In Python, le funzioni sono considerate **oggetti di prima classe**. Questo significa che puoi trattarle esattamente come qualsiasi altra variabile (come un numero o una lista):\n", + "\n", + "1. **Possono essere assegnate a variabili** (diventano un alias).\n", + "2. **Possono essere passate come argomento** ad altre funzioni (*Higher-Order Functions*).\n", + "3. **Possono essere ritornate** da altre funzioni.\n", + "\n", + "Questo modello flessibile è il fondamento dei *decorator* e del *functional programming* in Python.\n", + "\n", + "### Esempio di Assegnazione e Passaggio come Argomento" ] }, { @@ -36,19 +41,28 @@ "def greet(name):\n", " return f\"Hello, {name}!\"\n", "\n", - "# Assigning the function to a variable\n", + "# 1. Assigning the function to a variable\n", "say_hello = greet\n", + "print(say_hello(\"Pythonista\"))\n", + "\n", + "def execute_func(func, arg):\n", + " return func(arg)\n", "\n", - "print(say_hello(\"Pythonista\"))" + "# 2. Passing the function as an argument\n", + "print(execute_func(greet, \"Tester\"))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ + "--- \n", + "\n", "## 🧱 Funzioni annidate (Nested Functions)\n", "\n", - "Una funzione può essere **definita all'interno** di un'altra funzione. Questo è utile per incapsulare logica o creare funzioni di supporto locali." + "Una funzione può essere **definita all'interno** di un'altra funzione. Questo è utile per incapsulare logica o creare funzioni di supporto locali, in quanto la funzione interna è visibile **solo** all'interno della funzione esterna.\n", + "\n", + "**Nota Importante (Scope):** La funzione interna ha accesso alle variabili definite nello *scope* della funzione esterna, anche se non le prende come argomenti. Questo meccanismo è il preludio alle *closure*." ] }, { @@ -57,23 +71,33 @@ "metadata": {}, "outputs": [], "source": [ - "def outer_function():\n", - " def inner_function():\n", - " print(\"Inner function executed!\")\n", + "def outer_function(message):\n", + " \n", + " def inner_function(): # Nested function\n", + " print(f\"Inner function executed! The message is: {message}\")\n", " \n", " print(\"Outer function running...\")\n", " inner_function()\n", "\n", - "outer_function()" + "outer_function(\"Test a nested function\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ + "--- \n", + "\n", "## 🌀 Closure\n", "\n", - "Una **closure** è una funzione che ricorda i valori delle variabili dell’ambiente in cui è stata creata, anche dopo che quell’ambiente è stato distrutto." + "Una **closure** è una funzione annidata che **ricorda i valori delle variabili** dell’ambiente (scope) in cui è stata creata, **anche dopo che quell’ambiente (la funzione esterna) è stato distrutto** (ha terminato l'esecuzione).\n", + "\n", + "Le tre condizioni per una closure sono:\n", + "1. C'è una funzione annidata.\n", + "2. La funzione annidata fa riferimento a una variabile dallo scope della funzione esterna.\n", + "3. La funzione esterna ritorna la funzione annidata.\n", + "\n", + "Le closure permettono di creare **generatori di funzioni**, dove la funzione esterna configura un contesto (il valore di `x`) e la funzione interna lo utilizza in seguito." ] }, { @@ -84,11 +108,59 @@ "source": [ "def make_multiplier(x):\n", " def multiplier(y):\n", - " return x * y # remembers the value of x\n", + " return x * y # closure: remember value of 'x'\n", " return multiplier\n", "\n", + "# 2 different functions (closure) with different scope for 'x'\n", "times3 = make_multiplier(3)\n", - "print(times3(5)) # 15" + "times5 = make_multiplier(5)\n", + "\n", + "print(f\"times3(5) = {times3(5)}\") # 15 (x=3)\n", + "print(f\"times5(5) = {times5(5)}\") # 25 (x=5)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "--- \n", + "\n", + "## 🎨 Decorator personalizzati\n", + "\n", + "Un **decorator** è una funzione che prende un'altra funzione e ne estende il comportamento **senza modificarne il codice originale**. Sfrutta in maniera intensiva i concetti di *funzioni di prima classe* e *closure*.\n", + "\n", + "Sintassi base:\n", + "```python\n", + "@decorator_name\n", + "def my_function():\n", + " ...\n", + "```\n", + "\n", + "Questa sintassi è solo *syntactic sugar* (zucchero sintattico) per:\n", + "```python\n", + "my_function = decorator_name(my_function)\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def log_call(func):\n", + " def wrapper(*args, **kwargs):\n", + " print(f\"Calling {func.__name__}...\")\n", + " result = func(*args, **kwargs)\n", + " print(f\"{func.__name__} finished!\")\n", + " return result\n", + " return wrapper\n", + "\n", + "@log_call\n", + "def say_hi():\n", + " print(\"Hi there!\")\n", + "\n", + "say_hi()" ] }, {