{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "

6: Funzioni e moduli

\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "## 6.1 Introduzione\n", "\n", "Le funzioni ci permettono di raggruppare una sequenza di comandi in un blocco logico. La funzione è identificata da un nome in modo da poter essere \"chiamata\". Comunichiamo con una funzione attraverso una interfaccia ben definita, fornendo dei parametri, e ricevendo di ritorno delle informazioni. Generalmente non sappiamo esattamente come una funzione ottiene il valore che ci restituisce, conosciamo solo l'interfaccia.\n", "\n", "Per esempio la funzione `math.sqrt`: non sappiamo esattamente come calcola la radice quadrata, ma conosciamo l'interfaccia: se passiamo il valore *x* alla funzione, ci restituisce (un valore approssimato per) $\\sqrt{x}$.\n", "\n", "Possiamo raggruppare diverse funzioni in un modulo di Python e creare le nostre librerie di utilità." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Il format generico della definizione di una funzione è:\n", "\n", "```python\n", "def my_function(arg1, arg2, ..., argn):\n", " \"\"\"Optional docstring.\"\"\"\n", "\n", " # Implementation of the function\n", "\n", " return result # optional\n", "\n", "#this is not part of the function\n", "some_command\n", "```\n", "\n", "Le parentesi dopo il nome della funzione sono necessarie." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Per funzioni definite dall'utente (classi, tipi, moduli, …), dovrebbe sempre essere presente una docstring sintetica ma esauriente. Sei mesi dopo aver scritto un pezzo di codice, anche l'autore ha difficoltà a comprenderlo senza un buon apparato di commenti. \"Se non l'hai documentato, non l'hai fatto\": severo ma giusto.\n", "\n", "Come documentare una funzione definita dall'utente:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def power2and3(x):\n", " \"\"\"Returns the tuple (x**2, x**3)\"\"\"\n", " return x**2 ,x**3\n", "\n", "power2and3(2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", " \n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "La documentazione può essere recuperata con il comando `help`" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "help(power2and3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Possiamo definire più funzioni di Python in un singolo file e utilizzare una funzione all'interno di una funzione diversa. Ecco un esempio:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def returnstars( n ):\n", " return n * '*'\n", "\n", "def print_centered_in_stars( string ):\n", " linelength = 46 \n", " starstring = returnstars((linelength - len(string)) // 2)\n", "\n", " print(starstring + string + starstring)\n", "\n", "print_centered_in_stars('Hello world!')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 6.2 Valori di default e parametri opzionali\n", "\n", "Python permette di definire valori di *default* per gli argomenti di una funzione. Ecco un esempio: la funzione `myfunc` prende tre argomenti: `x`, `p` e `debug`. Il primo argomento `x` è una variabile “posizionale”; deve essere presente nella chiamata alla funzione. Il secondo argomento `p` ha il valore di default 2, il terzo `debug` ha come valore di default False. Questi argomenti sono opzionali: se l'utente chiama questa funzione con un solo argomento, l'argomento viene assegnato a `x` mentre `p` e `debug` assumeranno i valori di default. Se vengono forniti due argomenti (senza nome) il primo viene assegnato a `x` e il secondo a `p` mentre `debug` assumera il valore di default e così via. Quindi è possibile passare un parametro opzionale senza utilizzare la keyword, tuttavia il modo migliore e più comprensibile di usare i parametri di default è quello di utilizzare espicitamente la keyword che li individua. In questo modo non è necessario ripettare l'ordine degli argomenti." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def myfunc(x, p=2, debug=False):\n", " if debug:\n", " print(\"evaluating myfunc for x = \" + str(x) + \" using exponent p = \" + str(p))\n", " return x**p" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "myfunc(5)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "myfunc(5, 3)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "myfunc(5, debug=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Anche gli argomenti posizionali possono essere passati per nome:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "myfunc(p=3, debug=True, x=7) " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", " \n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Se non vengono passati **tutti** i parametri posizionali si ha un errore:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "myfunc(p=3, debug=True) " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Nella definizione di una funzione gli argomenti con nome devono venire dopo tutti gli argomenti posizionali. In caso contrario\n", "si ha un errore quando la funzione viene caricata in memoria:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def pippo(message='Hello ', name):\n", " print(message,name)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Per esempio `help(print)` restituisce:" ] }, { "cell_type": "raw", "metadata": {}, "source": [ "Help on built-in function print in module builtins:\n", "\n", "print(...)\n", " print(value, ..., sep=' ', end='\\n', file=sys.stdout, flush=False)\n", " \n", " Prints the values to a stream, or to sys.stdout by default.\n", " Optional keyword arguments:\n", " file: a file-like object (stream); defaults to the current sys.stdout.\n", " sep: string inserted between values, default a space.\n", " end: string appended after the last value, default a newline.\n", " flush: whether to forcibly flush the stream.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 6.3 Funzioni con un numero variabile di argomenti: `*args` e `**kwargs`" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Abbiamo visto che in Python possiamo accedere agli elementi di un oggetto iterabile (lista, ntupla, stringa, ...) attraverso gli indici:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "en = (3,4,5,6,7,8)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "en[0]" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "en[1:4]" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "en[-1]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Possiamo anche assegnare gli elementi di un iterabile a variabili singole, operazione che si chiama spacchettamento,\n", "purchè il numero di variabili corrisponda al numero di elementi dell'iterabile:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "a,b,c,d,e,f = en\n", "print(f\"c:{c}, a:{a}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### L'operatore di impacchettamento/spacchettamento `*` per sequenze\n", "\n", "L'operatore `*` ci permette di essere flessibili, definendo un oggetto con un numero variabile di elementi.
\n", "Nell'esempio seguente `en_1` e `en_2` catturano i primi due elementi della lista. `en_last` cattura l'ultimo elemento.\n", "Tutti gli altri elementi, qualunque sia il loro numero, vengono catturati\n", "da `*en2_rest`. La variabile `en2_rest` (senza `*`) contiene la lista degli elementi dall'indice 2 a len(en)-1." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "en_1, en_2, *en_rest, en_last = en\n", "\n", "print(f\"en_1:{en_1}, en_2:{en_2}, en_last:{en_last}, en_rest:{en_rest}\\n\")\n", "print(f\"Il tipo di en_rest è {type(en_rest)}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Questa notazione funziona anche se non ci sono elementi da spacchettare:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "a,*b = [1]\n", "print(f\"a = {a}, b = {b}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "L'operatore `*` permette anche di unire facilmente sequenze:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "a = [1,3,5]\n", "b = (7.3,8.1)\n", "c = [*a,*b]\n", "c" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "L'operatore di impacchettamento/spacchettamento `*` permette di costruire funzioni con un numero indefinito di argomenti usando la notazione:\n", "```python\n", "def my_function(*args):\n", "```\n", "Tradizionalmente si usa il nome `args`, ma come sempre i nomi degli argomenti di una funzione può essere qualunque." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Definire una funzione con *args\n", "def show_countries(*args):\n", " print(f\"The args type: {type(args)}\")\n", " for item in args:\n", " print(item)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Chiamata alla funzione con due argomenti \n", "show_countries('America', 'Canada')" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Chiamata alla funzione con tre argomenti \n", "show_countries('America', 'Canada', 'Mexico')" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Usando una ntupla senza unpacking. Stiamo passando un solo argomento\n", "inp = ('America', 'Canada')\n", "show_countries(inp)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Usando una tuple spacchettandola con *\n", "inp = ('America', 'Canada')\n", "show_countries(*inp)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Esempi\n", "\n", "Una funzione che usa l'impacchettamento per sommare su un numero arbitrario di argomenti:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def mySum(*args):\n", "\treturn sum(args)\n", "\n", "# Chiamata\n", "print(mySum(1, 2, 3, 4, 5))\n", "print(mySum(10, 20))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "L'operatore `*` si può utilizzare nella chiamata a una funzione con numero fisso di argomenti purchè questa operazione produca il numero corretto di argomenti:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Una semplice funzione che prende tre argomenti e li stampa\n", "def fun1(a, b, c):\n", "\tprint(a, b, c)\n", "\n", "# arg_list viene spacchettato (in questo caso deve contenere esattamente tre elementi)\n", "# e il suo contenuto viene passato a fun1\n", "arg_list = ['Hello', 'beautiful', 'world!']\n", "\n", "fun1(*arg_list)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Un'altra funzione che prende un numero arbitrario di argomenti impacchettati nella tuple *args." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def fun2(*args):\n", " # La tuple args viene convertita in lista per poterne modificare gli elementi\n", " args = list(args)\n", "\t# Gli elementi vengono modificati. Devono essere presenti almeno due elementi\n", " args[0] = 'Greetings,'\n", " args[1] = 'awesome'\n", " return args\n", " \n", "output = fun2('Hello', 'beautiful', 'world!', 'Today','is', 'gorgeous.')\n", "print(output)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### L'operatore di impacchettamento/spacchettamento, `**`, per argomenti con nome e dizionari" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "L'operatore di impacchettamento/spacchettamento `**` permette di costruire funzioni con un numero indefinito di argomenti con nome usando la notazione:\n", "```python\n", "def my_function(**kwargs):\n", "```\n", "Il nome `kwargs` è puramente tradizionale." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Un esempio in cui un gruppo di argomenti con nome viene impacchettato in un dizionario usando **\n", "def fun(**kwargs):\n", " # kwargs is a dict\n", " print(type(kwargs))\n", " # Printing dictionary items\n", " for key in kwargs:\n", " val = kwargs[key]\n", " print(f\"{key} = {val}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Chiamata a `fun` con un argomento:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fun(language=\"Python\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Chiamata a `fun` con tre argomenti:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fun(name=\"geeks\", ID=\"101\", language=\"Python\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Oppure:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "my_kwargs = dict(name=\"geeks\", ID=\"101\", language=\"Python\")\n", "fun(**my_kwargs)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "L'operatore `**` può essere utilizzato anche per creare o unire dizionari. Nell'esempio seguente creiamo un dizionario `voti0` e poi usiamo `**` per creare una copia indipendente (profonda) `voti1` di `voti0`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "voti0 = {'Chiara':29, 'Roberto':24}\n", "voti1 = {**voti0}" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(f\" voti0 : {voti0}, voti1 : {voti1}\")\n", "print(f\" id(voti0) : {id(voti0)}, id(voti1) : {id(voti1)}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Se uniamo due dizionari che non hanno chiavi in comune il risultato è la somma dei due risultati. Se ci sono chiavi in comune,\n", "i valori saranno quelli dell'ultimo dizionario. Un esempio per chiarire:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "d1 = {'a': 1, 'b': 2}\n", "d2 = {'c': 3, 'd': 4}\n", "d2a = {'b': 20, 'c': 3, 'd':4}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`d1` e `d2` non hanno chiavi comuni:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "d_unione1 = {**d1, **d2}\n", "d_unione1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`d1` e `d2a` hanno in comune la chiave `'b'`. Se uniamo `d2a` a `d1` la chiave `'b'`viene associata al suo valore in `d2a`. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "d_unione2 = {**d1, **d2a}\n", "d_unione2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Se uniamo `d1` a `d2a` la chiave `'b'` viene associata al suo valore in `d1`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "d_unione3 = { **d2a, **d1}\n", "d_unione3" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Caso più generale: `*args` + `**kwargs`\n", "Nella chiamata a una funzione tutti gli argomenti posizionali devono precedere gli argomenti con nome: `*args,**kwargs` e NON `**kwargs,*args`" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def genfun(*args,**kwargs):\n", " for i,arg in enumerate(args):\n", " print('index:', i,'value:',arg)\n", " print('---------------')\n", " for (key,value) in kwargs.items():\n", " print('key:',key,' ','value:',value)\n", " \n", "# Argomenti\n", "positional_args = (10,20,30)\n", "keyword_args = {'pippo':'cane', 'minnie':'topo'} # Le chiavi devono obbligatoriamente essere stringhe \n", "\n", "# Chiamata\n", "genfun(*positional_args,**keyword_args)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Per approfondire: https://realpython.com/python-kwargs-and-args/" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 6.4 Cosa succeede quando si passano argomenti a una funzione in Python\n", "\n", "In Python, gli oggetti sono passati per referenza (tipo pointer) all'oggetto. A seconda di come la referenza viene usate nella funzione e del tipo di oggetto che viene referenziato, questo può significare che qualsiasi modifica, all'interno della funzione, dell'oggetto passato si riflette immediatamente all'esterno.\n", "\n", "Tre esempi per chiarire. Iniziamo passando una lista a una funzione che itera sugli elementi della sequenza raddoppiando il valore di ciascuno:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def double_the_values(l):\n", " print(f\"in double_the_values: l = {l}\")\n", " for i in range(len(l)):\n", " l[i] = l[i] * 2\n", " print(f\"in double_the_values: changed l to l = {l}\")\n", "\n", "l_global = [0, 1, 2, 3, 10]\n", "print(f\"In main: s = {l_global}\")\n", "double_the_values(l_global)\n", "print(f\"In main: s = {l_global}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Quando la funzione viene chiamata con argomento `pippo`, le viene passata una referenza all'oggetto `pippo` che deve essere definito all'esterno della funzione. `l` diventa sinonimo di `pippo`. La linea `l[i] = l[i] * 2` calcola il membro di destra leggendo l'elemento di indice `i`e poi moltiplicandolo per due. Una referenza a questo nuovo oggetto viene immagazzinata\n", "nell'oggetto lista `l` alla posizione di indice `i`. È stato quindi modificato l'oggestto lista, che è referenziato attraverso il nome `l`.\n", "\n", "La referenza all'oggetto lista non cambia mai: la linea `l[i] = l[i] * 2` cambia l'elemento `l[i]` della lista `l` ma non cambia mai la referenza `l` per la lista. Quindi sia la funzione che il programma che la chiama operano sullo stesso oggetto, rispettivamente attraverso la referenza `l` e `global_l`.\n", "\n", "Al contrario, nell'esempio seguente la lista globale non viene modificata\n", "all'interno della funzione:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def double_the_list(l):\n", " print(f\"in double_the_list: l = {l}\")\n", " l = l + l\n", " print(f\"in double_the_list: changed l to l = {l}\")\n", "\n", "l_global = \"Hello\"\n", "print(f\"In main: s = {l_global}\")\n", "double_the_list(l_global)\n", "print(f\"In main: s = {l_global}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Quello che suucede in questo caso è che durante la valutazione di `l = l + l` viene creato un nuovo oggetto che contiene `l + l`, a cui successivamente legato il nome `l`. In questo processo, si perde la referenza all'oggetto lista `l` che è stato passato alla funzione che quindi non viene modificata.
\n", "Questo è vero anche per un oggetto mutabile come una lista." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "l_global = [1,2]\n", "print(f\"In main: s = {l_global}\")\n", "double_the_list(l_global)\n", "print(f\"In main: s = {l_global}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Infine, vediamo che output produce il programma che segue:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def double_the_value(l):\n", " print(f\"in double_the_values: l = {l}\")\n", " l = 2 * l\n", " print(f\"in double_the_values: changed l to l = {l}\")\n", "\n", "# 42 è immutabile\n", "l_global = 42\n", "print(f\"In main: s = {l_global}\")\n", "double_the_value(l_global)\n", "print(f\"In main: s = {l_global}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Anche in questo esempio, raddoppiamo il valore (da 42 a 84) all'interno della funzione. Tuttavia, quando colleghiamo l'oggetto 84 al nome python `l` (nella riga `l = l * 2`) abbiamo creato un nuovo oggetto (84), e poi leghiamo il nuovo oggetto a `l`. Nuovamente, In questo processo, si perde la referenza all'oggetto 42 all'interno della funzione. Questo non modifica l'oggetto 42 in sè, né la sua referenza `l_global`." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In conclusione, il comportamento di Python per quanto riguarda gli argomenti passati a una funzione può sembrare diverso nei vari casi. Tuttavia, si tratta sempre di chiamata per referenza e il comportamento può essere spiegato\n", "in ogni caso con lo stesso ragionamento." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 6.5 Passare funzioni come argomenti a funzioni\n", "\n", "Le funzioni sono oggetti in Python, quindi è possibile passare funzioni come argomenti ad altre funzioni. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def sum(a,b):\n", " return a+b\n", "\n", "def product(a,b):\n", " return a*b\n", "\n", "def operation(f,a,b):\n", " return f(a,b)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "operation(sum,3,6)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "operation(product,3,6)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 6.6 Moduli\n", "\n", "I Moduli\n", "\n", "- Raggruppano funzionalità\n", "\n", "- Forniscono namespaces (Insieme di simboli riconosciuti dal kernel)\n", "\n", "- La standard library di Python contiene aun gran numero di moduli - “Pronti per l'uso”\n", "\n", "- Provate a digitare `help(’modules’)`\n", "\n", "- Forniscono il modo per estendere Python\n", "\n", "### 6.6.1 Come importare moduli" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import math" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Questo introduce il nome `math` nel namespace del processo in cui il comando import è stato eseguito. I nomi delle funzioni contenute nel modulo `math` non vengono introdotti nel namespace: devono essere invocati attraverso il nome `math`. Per: `math.sin`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import math, cmath" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Si può importare più di un modulo con un solo comando, anche se la \n", "[Python Style Guide](http://www.python.org/dev/peps/pep-0008/) raccomanda di non farlo. È preferibile scrivere:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import math\n", "import cmath\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import math as mathematics" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Il nome con cui un modulo è conosciuto localmente può essre diverso dal suo nome “ufficiale”. In genere lo si fa\n", "\n", "- Per evitare conflitti conflitti con nomi già esistenti\n", "\n", "- Per cambiare il nome ufficiale in uno più maneggevole. Per esempio `import SimpleHTTPServer as shs`. Questa pratica viene scoraggiata per il production code (in genere nomi \"descrittivi\" rendono i programmi molto più comprensibili di nomi brevi e criptici), ma nella fase di esplorazione e test, utilizzare sinonimi brevi ci semplifica la vita\n", "\n", "Esempi tipici e ormai universali sono `import numpy as np`, `import matplotlib.pyplot as plt`. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from math import sin" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Questo comando importa la funzione `sin` dal modulo `math`, ma non introduce il nome `math` nel namespace. Introduce solo il nome `sin`. È possibile importare più nome con un solo comando:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from math import sin, cos" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Dopo questo `import` la funzione `sin` può essere chiamata nel modo seguente:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sin(1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Si noti che anche in Numpy c'è la funzione `sin`. Se questa venisse importata con `from numpy import sin` maschererebbe la funzione importata in precedenza da `math`. Provate a farlo e poi eseguite `help(sin)`." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Per concludere, guardiamo questa notazione:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from math import *" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Di nuovo, questo comando non introduce il nome `math` nel namespace. Introduce tuttavia nel namespace *tutti i nomi pubblici* contenuti nel modulo `math`. In genere, è una pessima idea:\n", "\n", "- Un gran numero di nuovi nomi viene scaricato nel namespace attuale.\n", "\n", "- Siete sicuro che nessuno dei nuovi nomi sostituisca un nome già presente?\n", "\n", "- Diventa molto difficile tenere traccia della provenienza di tutti questi nomi\n", "\n", "- Detto questo, alcuni moduli (compreso qualcuno nella standard library), raccomandano di essere importati in questo modo. Usate con cautela!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 6.6.2 Come creare moduli\n", "\n", "Un modulo non è altro che un file Python. Ecco un esempio di modulo che potete salvare in un file chiamato `module1.py`:\n", "\n", "```python\n", "def someusefulfunction():\n", " pass\n", "\n", "print(\"My name is\", __name__)\n", "```\n", "\n", "Possiamo eseguire questo (modulo) file come un normale programma Python (per esempio `python module1.py`):" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%pwd" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%cd ../ShellPrograms/" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%ls" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "!python module1.py" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notiamo che la variabile \"magica\" di Python `__name__` prende il valore `__main__` quando il file `module1.py` viene eseguito.\n", "\n", "D'altra parte, possiamo *importare* `module1.py` in un altro file (che potrebbe chiamarsi `prog1.py`), in questo modo:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "```\n", "import module1 #nel file prog1.py\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Provate a fare queste operazioni per conto vostro, nella vostra home directory, invece che all'interno del notebook!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Sneak peek: dall'interno del notebook si fa come segue:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "!python prog1.py" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Quando Python incontra il comando `import module1` in `prog1.py`, cerca il file `module1.py` nella working directory attuale (se non la trova cerca in tutte le directory in `sys.path`), e apre il file `module1.py`. Mentre legge il file `module1.py` da cima a fondo, aggiunge qualsiasi definizione di funzione contenuta nel file all'interno del namespace nel contesto da cui `module1.py` è stato chiamato (In questo caso il programma principale in `prog1.py`). In questo esempio,c'è solo la funzione `someusefulfunction`. Quando il processo di import process è completato, possiamo utilizzare `module1.someusefulfunction` in `prog.py`. Se Python incontra comandi diversi da definizione di funzioni (e classi) nell'importare `module1.py`, li esegue immediatamente. In questo caso, trova il comando `print(My name is, __name__)`." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notate l'output diverso se *importiamo* `module1.py` piuttosto che eseguirlo da solo: se il file viene importato, `__name__` all'interno del modulo prende come valore il nome del modulo stesso." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Nozioni più avanzate: importare un modulo usando il full path" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import importlib.util\n", "spec = importlib.util.spec_from_file_location(\"module1\", \"/Users/maina/python/MyCourse2/ShellPrograms/module1.py\")\n", "foo = importlib.util.module_from_spec(spec)\n", "spec.loader.exec_module(foo)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 6.6.3 Uso di \\_\\_name\\_\\_\n", "\n", "Riassumendo,\n", "\n", "- `__name__` vale `__main__` se il file viene eseguito da solo\n", "\n", "- `__name__` vale il nome del modulo (cioè il nome del file che contiene il modulo senza il suffisso `.py`) se il modulo viene importato.\n", "\n", "È possibile quindi utilizzare la costruzione `if` seguente in `module1.py` per scrivere del codice che viene eseguito *soltanto* quando il modulo viene eseguito da solo. Questo è utile per includere programmi di test o esemplificazioni delle capacità di un modulo nella parte \"sotto condizione\" del programma principale. È pratica comune che qualunque\n", "file contenente un modulo contenga anche un programma principale all'interno dell'`if` che mostra come utilizzare il modulo e quali capacità fornisca." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", " \n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Esempio 3\n", "\n", "Il prossimo esempio mostra un main program nel modulo `vectools.py` che dimostra l'uso delle funzioni definite nel file:\n", "\n", "```python\n", "import numpy as np\n", "\n", "\n", "def norm(x):\n", " \"\"\"returns the magnitude of a vector x\"\"\"\n", " return np.sqrt(np.sum(x ** 2))\n", "\n", "\n", "def unitvector(x):\n", " \"\"\"returns a unit vector x/|x|. x needs to be a numpy array.\"\"\"\n", " xnorm = norm(x)\n", " if xnorm == 0:\n", " raise ValueError(\"Can't normalise vector with length 0\")\n", " return x / norm(x)\n", "\n", "\n", "if __name__ == \"__main__\":\n", " #a little demo of how the functions in this module can be used:\n", " x1 = np.array([0, 1, 2])\n", " print(\"The norm of \" + str(x1) + \" is \" + str(norm(x1)) + \".\")\n", " print(\"The unitvector in direction of \" + str(x1) + \" is \" \\\n", " + str(unitvector(x1)) + \".\")\n", "```\n", "\n", "Se questo file viene eseguito con `python vectools.py`, allora `__name__==__main__` è vero, e l'output sarà:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "!pwd\n", "!python ../ShellPrograms/vectools.py" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Se il file viene importato (cioè usato come un modulo) in un altro file python, allora `__name__==__main__` è falso, e i comandi all'interno dell'`if` non viene eseguito (e nessun output viene prodotto)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", " \n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Esempio 4\n", "\n", "Anche se un programma Python non è concepito per essere usato come un modulo, è buona abitudine (e di uso comune) scrivere il programma principale all'interno della condizione `if __name__ == \"__main__\"`:\n", "\n", "- capita spesso che le funzioni contenute nel file possano essere riutilizzate (risparmiando lavoro). Notate che questo incoraggia fortemente a spezzare i programmi in funzioni.\n", "\n", "- è utile per il \"regression testing\", il controllo che modifiche ad un codice funzionante non introducano errori. Si effettua eseguendo dei programmi di test che in precedenza funzionavano correttamente \n", "\n", "Supponiamo di dover scrivere una funzione che restituisca i cinque più piccoli numeri primi, e che inoltre, li stampi. (Il compito in questo caso è banale, ma possiamo immaginare situazioni più complesse). Si potrebbe essere tentati di scrivere:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def primes5():\n", " return (2, 3, 5, 7, 11)\n", "\n", "for p in primes5():\n", " print(f\"{p}\", end=' ')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "È però preferibile racchiudere la funzione principale sotto condizione:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def primes5():\n", " return (2, 3, 5, 7, 11)\n", "\n", "if __name__==\"__main__\":\n", " for p in primes5():\n", " print(f\"{p}\", end=' ')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In [Molti modi per calcolare una somma](Compiti_Frequenti.ipynb#Molti-modi-di-calcolare-unaa-somma) trovate altri esempi di questa tecnica. Includere funzioni con nomi che iniziano con `test_` rende possibile utilizzare ilframework di regression testing py.test (see ).\n", "\n", "#### Ulteriori informazioni\n", "\n", "- [Python Tutorial Section 6](http://docs.python.org/tutorial/modules.html#modules)" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "6.7 Eseguire Python al di fuori di un Notebook\n", "-----------------------------------------------\n", "\n", "### Eseguire un comando o uno script dallo shell di Python\n", "\n", "Per lanciare lo shell di Python, dalla linea di comando eseguire (assumendo che il \"prompt\" di sistema sia \"$\"):\n", "\n", "```bash\n", "$ python \n", "```\n", "\n", "La risposta dovrebbe essere simile a:\n", "\n", "```bash\n", "Python 3.7.1 (default, Dec 14 2018, 13:28:58) \n", "[Clang 4.0.1 (tags/RELEASE_401/final)] :: Anaconda, Inc. on darwin\n", "Type \"help\", \"copyright\", \"credits\" or \"license\" for more information.\n", ">>>\n", "```\n", "\">>>\" è il \"prompt\" di Python in modalità interattiva.\n", "\n", "Python è un linguaggio *interpretato*. \n", "I comandi vengoni immediatamente interpretati ed eseguiti dal Python interpreter (Interactive Mode). Questo è molto utile per lo studente/ programmatore per capire come usare un certo comando (magari prima di inserire il comando in un programma Python complesso). Il modo di procedere di Python può essere descritto come:
\n", "\n", " Leggere il comando --> Eseguirlo --> Stampare il risultato ottenuto --> ripetere il ciclo(Loop)\n", "\n", "in inglese REPL (Read-Execute-Print-Repeat).\n", "\n", "\n", "È possibile digitare un comando e eseguirlo premendo il tasto Invio:\n", "\n", ">>> 2 + 2\n", "4\n", "\n", "Possiamo raccogliere una serie di comandi in file di testo e salvarlo su disco come un *Python program* (Normal Mode). Convenzionalmente questi file hanno l'estensione“`.py`”, per esempio `hello.py`.\n", "\n", "Per eseguire lo script pippo.py in modalità interattiva si può dare il comando:\n", "```bash\n", ">>> import pippo \n", "```\n", "\n", "Per uscire dalla modalità interattiva digitare:\n", "```bash\n", ">>> quit() \n", "```\n", " \n", "### Eseguire uno script dalla linea di comando\n", "\n", "Per eseguire lo script pippo.py dalla linea di comandi, senza attivare la modalità interattiva, si può dare il comando:\n", "```bash\n", "$ python pippo.py \n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", " \n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "

Imparare Facendo

\n", " Create un file test.py nella cartella ShellPrograms usando un editor di programmazione (Per esempio Notepad su Windows o TextEdit su Mac). In test.py inserite i comandi per:\n", "
    \n", "
  1. assegnare il valore 17 alla variabile a;\n", "
  2. assegnare alla variabile b il quadrato di a;\n", "
  3. stampare il valore di b. \n", "
\n", "Salvate e chiudete il file. Aprite una finestra di comando ed eseguite il file con il comando python test.py.\n", "
\n", "Eseguite il comando python per lanciare lo shell di python. Eseguite lo script con il comando import test.py\n", "
\n", " Uscite dallo shell di python con il comando quit().\n", "
" ] } ], "metadata": { "hide_input": false, "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.9.12" }, "toc": { "base_numbering": 1, "nav_menu": {}, "number_sections": true, "sideBar": true, "skip_h1_title": true, "title_cell": "Table of Contents", "title_sidebar": "Contents", "toc_cell": false, "toc_position": {}, "toc_section_display": true, "toc_window_display": false }, "varInspector": { "cols": { "lenName": 16, "lenType": 16, "lenVar": 40 }, "kernels_config": { "python": { "delete_cmd_postfix": "", "delete_cmd_prefix": "del ", "library": "var_list.py", "varRefreshCmd": "print(var_dic_list())" }, "r": { "delete_cmd_postfix": ") ", "delete_cmd_prefix": "rm(", "library": "var_list.r", "varRefreshCmd": "cat(var_dic_list()) " } }, "types_to_exclude": [ "module", "function", "builtin_function_or_method", "instance", "_Feature" ], "window_display": false } }, "nbformat": 4, "nbformat_minor": 4 }