{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "# Creare Script e moduli\n",
    "[⬇️ Scarica il file](./Python-2026-creare-script-e-moduli.ipynb)\n",
    "\n",
    "--- \n",
    "\n",
    "## Script in Python\n",
    "Uno script è un file contenente istruzioni che il computer esegue **automaticamente** in base ad un **interprete**.  \n",
    "L'idea di uno script è quella di scrivere un programma in un file di testo, e poi eseguirlo senza doverlo copiare e incollare ogni volta in un terminale o in un ambiente interattivo. È possibile eseguire uno script automaticamente in modo che il computer possa eseguire un programma anche quando noi non siamo davanti al computer, ad esempio di notte, mentre siamo in vacanza o mentre stiamo facendo qualcosa di più piacevole, come studiare Fisica o andare a farci un giro.  \n",
    "In ricerca è normale avere programmi, che siano di analisi o di simulazione, che richiedono ore, giorni o addirittura settimane per arrivare ad un risultato soddisfacente. Chiaramente, questi non possono essere eseguiti in modo interattivo.\n",
    "\n",
    "### Creare uno script\n",
    "\n",
    "Su Mac e Linux, uno script Python è un file **eseguibile** contenente una serie di istruzioni Python, e che inizia con la riga \n",
    "```\n",
    "#!/usr/bin/env python3\n",
    "```\n",
    "Questa riga, chiamata `shebang`, dice al computer di usare un ambiente (\"environment\") python per interpretare ed eseguire tutto ciò che segue nel file. \n",
    "\n",
    "In alternativa, su qualsiasi sistema (Windows incluso) si può eseguire uno script contenuto in un file `script.py` tramite il comando\n",
    "```\n",
    "python3 script.py\n",
    "```\n",
    "dopo aver raggiunto la cartella che contiene il file `script.py` con il comando `cd` (change directory) da terminale.\n",
    "\n",
    "\n",
    "**Nota**: `script.py` è un nome preso come esempio. Potete scegliere il nome che volete: `ciccio.py`, `nonpotevoandareafareenologiacheaquestorastavoallapertoomagariallecantineferrari.py`, etc. \n",
    "Bisogna tenete a mente due cose importanti però\n",
    "1. Usate sempre l'estensione `.py` per chiarire che il file contiene codice Python.\n",
    "2. Usate nomi corti e comprensibili! Se non riuscite a trovare un nome corto opportuno è possibile che il vostro progetto abbia bisono di essere organizzato. Per esempio, se state simulando un sistema gravitazionale con un diverso numero di corpi ed avete un file `calcola_centro_di_massa_tra_corpi.py`, potrebbe aver senso creare una cartella `tre_corpi` ed inserire tutti i file relativi a tre corpi al suo interno."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Ciao Mondo.\n",
      " La somma di a+b fa 2 + 3 = 5 \n"
     ]
    }
   ],
   "source": [
    "## Un esempio. \n",
    "## Copiate il codice qui sotto in un file \"ciaomondo.py\". \n",
    "## Da terminale, eseguite ``chmod +x ciaomondo.py'' (le virgolette `` e '' non vanno scritte!).\n",
    "## Fatto questo eseguite ``./ciaomondo.py''\n",
    "## In alternativa, eseguite ``python3 ciaomondo.py''\n",
    "\n",
    "#!/usr/bin/env python3\n",
    "if __name__ == \"__main__\":\n",
    "    a = 2\n",
    "    b = 3\n",
    "    print(f\"Ciao Mondo.\\n La somma di a+b fa {a} + {b} = {a+b} \")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "> ### Esercizio - Creare il tuo primo script\n",
    ">\n",
    "> Seguite i passi del codice qui sopra:\n",
    "> 1. Aprite un editor di testo (VS Code, nano, vim, etc.)\n",
    "> 2. Copiate il codice dell'esempio \"Ciao Mondo\"\n",
    "> 3. Salvate il file come `ciaomondo.py` nella vostra directory di lavoro\n",
    "> 4. Su Mac/Linux: eseguite `chmod +x ciaomondo.py` seguito da `./ciaomondo.py`\n",
    "> 5. Su Windows: eseguite `python3 ciaomondo.py`\n",
    "> 6. Modificate il codice per stampare anche il prodotto `a * b` e il quoziente `a / b`\n",
    "> 7. Salvatelo con un nuovo nome (e.g., `operazioni.py`) e rieseguite"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## IMPORTANTE: Come l'interprete esegue gli script\n",
    "\n",
    "Quando eseguite uno script con l'interprete -- o una cella in un notebook -- succedono due cose.\n",
    "\n",
    "1. L'interprete verifica che la sintassi sia corretta. Questo lo fa scorrendo tutto il file.\n",
    "2. L'interprete esegue le righe **una alla volta**, controllando solo a questo punto che le funzioni e le variabili chiamate esistano! Questo vuol dire che potrebbe esserci un errore in una riga e questo potrebbe non venire rilevato perché la sua esecuzione dipende da condizioni che verificano solo raramente. Vediamo un esempio.\n",
    "\n",
    "\n",
    "```python\n",
    "#!/usr/bin/env python3\n",
    "#chmod +x ciao_mondo.py\n",
    "import numpy as np\n",
    "\n",
    "def ciao_mondo(a,b):\n",
    "    print(f\"Ciao Mondo.\\n La somma di a+b fa {a} + {b} = {a+b} \")\n",
    "\n",
    "\n",
    "if __name__ == \"__main__\":\n",
    "    a = 2\n",
    "    b = 3\n",
    "    ciao_mondo(a,b)\n",
    "    # selezioniamo un numero a caso tra 0 e 1\n",
    "    x = np.random.uniform() \n",
    "    if x>0.999:  \n",
    "        print(np.sin(a)) # questa condizione si verifica nel 0.1% dei casi\n",
    "    else:\n",
    "        print(sin(a)) # sin non esiste! Questa riga si esegue nel 99.9% dei casi\n",
    "```\n",
    "\n",
    "Nello script qui sopra la riga `print(sin(a))` non è corretta in quanto la funzione `sin` non è nota. Siccome abbiamo importato numpy chiamandolo `np` avremmo dovuto scrivere `np.sin`, come fatto nel ramo if. L'else però viene eseguito nel 99.9% dei casi, per cui nella maggior parte delle esecuzioni il nostro script fallirà con un errore `NameError: name 'sin' is not defined`. Questo è il tipo di errore che si nasconde durante testing superficiale e può causare problemi quando lasciamo che gli script vengano eseguiti in maniera automatica. Ricordate sempre di testare i vostri script in modo approfondito, e di controllare tutti i rami di esecuzione, anche quelli che si verificano raramente.\n",
    "\n",
    "**NOTA:** nei linguaggi compilati, come il `C`, questi errori sono meno frequenti perché la compilazione comporta un'analisi dettagliata di tutto il codice per poterlo poi trasformare in codice macchina."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Scripts che prendono argomenti\n",
    "Spesso vogliamo passare degli argomenti ad un programma, ad esempio il nome di un file da analizzare, o delle opzioni che specificano un comportamento particolare. Ad esempio da terminale possiamo stampare il contenuto di una directory `mydir` si usa il comando\n",
    "\n",
    "```\n",
    "ls mydir\n",
    "```\n",
    "\n",
    "dove `mydir` è un argomento per il programma `ls`. In Python questo comportamento si ottiene usando il modulo `sys`. Questo modulo fornisce la variabile `argv`, che è una lista contenente le stringhe passate alla riga di comando. Il numero di argomenti (equivalente a `argc` in C) si ottiene con `len(sys.argv)`.\n",
    "\n",
    "Ricapitolando:\n",
    "* `sys.argv`: lista contenente le stringhe passate alla riga di comando, incluso il nome dello script\n",
    "* `len(sys.argv)`: numero totale di argomenti (equivalente ad `argc` in linguaggi come C)\n",
    "* `sys.argv[0]` è sempre il nome dello script.\n",
    "\n",
    "Vediamo un esempio\n",
    "\n",
    "```python\n",
    "## Un secondo esempio. File con argomenti.\n",
    "## Copiate il codice qui sotto in un file \"ciaomondo_2.py\". \n",
    "## Da terminale, eseguite ``chmod +x ciaomondo_2.py'' (le virgolette `` e '' non vanno scritte!).\n",
    "## Fatto questo eseguite ``./ciaomondo_2.py''\n",
    "## In alternativa, eseguite ``python3 ciaomondo_2.py''\n",
    "\n",
    "#!/usr/bin/env python3\n",
    "import sys\n",
    "\n",
    "if __name__ == \"__main__\":\n",
    "    args = sys.argv   # lista degli argomenti da linea di comando. Il primo è il nome dello script!\n",
    "    argc = len(args)  # numero argomenti\n",
    "    if (argc < 3):\n",
    "        sys.stderr.write(\"Lo script richiede due parametri, i numeri a e b da sommare!\")\n",
    "        sys.stderr.write(f\"Esempio d'uso: {args[0]} 2 4\")\n",
    "        sys.stderr.write(f\"Aborting.\")\n",
    "        sys.exit(1)   # Esci dal programma con codice di errore 1 per indicare che qualcosa è andato storto\n",
    "\n",
    "    a = float(args[1])\n",
    "    b = float(args[2])\n",
    "    print(f\"Ciao Mondo.\\n La somma di a+b fa {a} + {b} = {a+b} \")\n",
    "    sys.exit(0) # esci ritornando zero per indicare che tutto è andato bene\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "tags": []
   },
   "source": [
    "## Moduli e pacchetti\n",
    "Un modulo non è altro che un file python (`.py`) che potete caricare tramite la parola chiave `import`. Un modulo può contenere:\n",
    "\n",
    "- Definizioni di funzioni e classi (e decoratori di conseguenza),\n",
    "- Definizioni di variabili,\n",
    "- Codice da eseguire nel momento in cui il modulo viene caricato.\n",
    "- Codice da eseguire nel caso il modulo venga eseguito direttamente da terminale.\n",
    "\n",
    "Per evitare che il codice venga eseguito da un `import`, si usa il seguente costrutto:\n",
    "```python\n",
    "if __name__ == \"__main__\":\n",
    "    #codice da eseguire tramite invocazione da shell\n",
    "```\n",
    "il modulo infatti avrà assegnato `__main__` come `__name__` solo se viene eseguito chiamando direttamente l'interprete dalla shell, o tramite\n",
    "```bash\n",
    "python3 module.py\n",
    "```\n",
    "\n",
    "oppure ponendo uno she-bang nella prima riga del file:\n",
    "```python\n",
    "#!/usr/bin/env python3\n",
    "```\n",
    "\n",
    "L'uso del comando di sistema `env` è importante perché questo chiama l'interprete attualmente in uso nell'environment python (quindi quello definito dal gestore di pacchetti, es. `virtualenv`, `conda`, oppure `mamba`).\n",
    "\n",
    "**Altre caratteristiche**\n",
    "- il modulo definisce un proprio namespace, ad esempio:\n",
    "```python\n",
    "import math\n",
    "math.sin()\n",
    "math.cos()\n",
    "```\n",
    "- il namespace può venire rinominato tramite `import <module> as <name>`\n",
    "```python\n",
    "import numpy as np\n",
    "np.sin()\n",
    "np.cos()\n",
    "```\n",
    "- il namespace può venire bypassato tramite `from <module> import *` (meglio non farlo)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Gli script funzionano anche come moduli\n",
    "Tutto quello che è fuori dall'if principale può venire caricato da import. Dopodiché possiamo usare il nostro file `.py` come un modulo, e usare la stessa sintassi che usiamo per i moduli python."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "import ciao_mondo"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Ciao Pippo!\n"
     ]
    }
   ],
   "source": [
    "ciao_mondo.ciao_pippo()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Ciao Pippo!\n"
     ]
    }
   ],
   "source": [
    "import ciao_mondo as cm\n",
    "cm.ciao_pippo()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "tags": []
   },
   "source": [
    "## Pacchetti\n",
    "Un pacchetto è una cartella contenente dei moduli o delle sottocartelle, ed un file `__init__.py`\n",
    "\n",
    "```\n",
    "└── my_package\n",
    "    ├── __init__.py\n",
    "    ├── analysis\n",
    "    │   ├── __init__.py\n",
    "    │   ├── analysis.py\n",
    "    │   └── data.py\n",
    "    └── prod\n",
    "        ├── MC.py\n",
    "        ├── PRNG.py\n",
    "        └── __init__.py\n",
    "```\n",
    "\n",
    "i files `__init__.py` possono essere tranquillamente vuoti, oppure possono specificare cosa caricare dal pacchetto, l'esecuzione di alcune funzioni, etc (vedere i libri consigliati). \n",
    "\n",
    "Ad esempio la variabile \n",
    "`__all__= [sin,cos,tan]` in `__init__.py` specifica quali oggetti vengono caricati dal comando `from module import *` \n",
    "Questo può essere molto importante per evitare di caricare troppi sottomoduli.\n",
    "\n",
    "Fate molta attenzione a [quanto scritto nel tutorial però](https://docs.python.org/3/tutorial/modules.html#importing-from-a-package).\n",
    "\n",
    "#### Usare i sottomoduli\n",
    "All'interno del pacchetto, o in generale se la directory in cui è contenuto è inclusa nella variabile `PYTHONPATH` (che è modificabile, vedi sotto), possiamo caricare i suoi sottomoduli come segue:\n",
    "```python\n",
    "import my_package.analysis.data\n",
    "```\n",
    "oppure\n",
    "```python\n",
    "from my_package.analysis import data\n",
    "```\n",
    "\n",
    "#### Riferimenti INTERNI ai pacchetti\n",
    "All'interno (e solo all'interno...) dei pacchetti i path di riferimento tra moduli funzionano come le directory su linux. Quindi nell'esempio precedente potete importare `data.py` da `analysis.py` chiamando\n",
    "```python\n",
    "from . import data\n",
    "``` \n",
    "e `MC.py` da `prod.py` come\n",
    "```python\n",
    "from ..prod import MC\n",
    "```\n",
    "\n",
    "#### Se non ci troviamo nella stessa cartella\n",
    "Se vogliamo accedere a moduli salvati in altre directory, dobbiamo dire a Python dove trovarli, modificando la variabile di sistema `PYTHONPATH`. \n",
    "\n",
    "```python\n",
    "import sys\n",
    "sys.path.insert(0,'/path/to/mod_directory')\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "celltoolbar": "Slideshow",
  "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.12.12"
  },
  "latex_envs": {
   "LaTeX_envs_menu_present": true,
   "autoclose": false,
   "autocomplete": true,
   "bibliofile": "biblio.bib",
   "cite_by": "apalike",
   "current_citInitial": 1,
   "eqLabelWithNumbers": true,
   "eqNumInitial": 1,
   "hotkeys": {
    "equation": "Ctrl-E",
    "itemize": "Ctrl-I"
   },
   "labels_anchors": false,
   "latex_user_defs": false,
   "report_style_numbering": false,
   "user_envs_cfg": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
