Questa è la continuazione della precedente
Parte 1 su
Python dove avevo illustrato alcune funzioni base tramite l'uso diretto della console interattiva (py.exe / python3).
In questa parte illustro la gestione delle eccezioni, l'uso delle funzioni, i moduli e le classi.
Uso dei file
L'uso della console interattiva va bene per piccole prove, appena si deve creare qualche cosa di più complesso conviene creare un
file, con estensione
.py dove scrivere il proprio codice.
L'esecuzione del codice avviene con la sintassi
# windows
py .\nome_file.py
# linux
python3 .\nome_file.py
Il modo migliore per gestire i file è usare
visual Studio Code
Visual Studio CodeEccezioni
Una parte importante di ogni programma è la corretta gestione delle eccezioni.
Python, per questo scopo, mette a disposizione le istruzioni
try except else finally nella forma
try:
# codice che potrebbe generare un eccezione
except:
# gestione dell'eccezione
else:
# opzionale
# eseguita solo se NON vengono sollevate eccezioni
finally:
# opzionale
# istruzioni eseguite sempre
# sia quando va tutto bene che dopo la gestione dell'eccezione
nella parte
except si possono gestire vari tipi di eccezioni, ad esempio
#x = 1 # prova a togliere il commento
y = 0 # prova con y = 0
try:
print(x / y)
except ZeroDivisionError as e:
# qui gestisco solo le eccezioni di tipo ZeroDivisionError
print("div by zero")
except:
# quella generica deve essere l'ultima
print("error")
else:
print("else")
finally:
print("finally")
questo codice stampa a video
- con x non definita => error, finally
- con x=1 e y=1 => 1.0, else, finally
- con x=1 e y=0 => div by zero, finally
Non vanno definite tutte le keyword, è possibile usare solo
try excepttabellina = input("Tabellina: ")
try:
t = int(tabellina)
for n in range(1, 11):
print(f"{n} x {t} = {n * t}")
except:
print('Numero non valido')
Volendo posso anche sollevare un eccezione da codice tramite il comando
raise nome_eccezione# sollevo un eccezione specifica
raise ValueError
# all'interno di except: rilancio l'eccezione corrente
try:
# eventuale errore
except:
print("errore")
# rilancio l'eccezione attuale
raise
Funzioni
Le funzioni in
Python vengono definite tramite la keywork
defdef nome_funzione(parametro1, parametro2, ...):
# corpo della funzione, con eventuale valore di ritorno
return valore
ad esempio la funzione
from datetime import datetime
def print_time(message, show_message = True):
if show_message == True:
print(f"{datetime.now()} {message}")
else:
print(datetime.now())
print()
Da notare il parametro show_message a cui è stato assegnato un valore di default.
Per invocare una funzione si usa la sintassi
nome_funzione(eventuali_parametri)# ometto il parametro con il valore di default
print_time('Ora')
# richiamo la funzione passando i parametri per nome
print_time(message='test', show_message=False)
# in questo caso l'ordine dei parametri non è importante
print_time(message='test', show_message=False)
l'esempio produce questo risultato
2019-10-10 22:04:30.746470 Ora
2019-10-10 22:04:30.747469
2019-10-10 22:04:30.748468
Attenzione, la funzione deve essere definita nel file prima che venga richiamata, altrimenti viene sollevata un eccezione di funzione non definita:
Traceback (most recent call last):
File ".\app.py", line 13, in <module>
print_time('Ora')
NameError: name 'print_time' is not defined
Argomenti infiiti
E' possibile definire una funzione per rifare in modo che accetti un numero
variabile di parametri tramite
*argvdef fn_multi(p1, *argv):
print("p1", p1)
for p in argv:
print(p)
fn_multi("ciao", "come", "va", "altri parametri", "...")
da come risultato
p1 ciao
come
va
altri parametri
...
Oppure posso usare
**kwargs per passare dei parametri in numero variabile per
nomedef fn_multi(**kwargs):
for key, value in kwargs.items():
print(key, value)
fn_multi(primo="ciao", par1="come", parN="va")
da come risultato
primo ciao
par1 come
parN va
Da notare la sintassi del for per ciclare sia sul nome del parametro (key) che sul valore (value)
Moduli
Quando il codice diventa complesso, è
impensabile gestire tutto in un unico file, conviene suddividere in codice in più parti, dette
moduli, da richiamare quando necessario.
Ogni modulo è un diverso file con estensione
.py.
Ad esempio possiamo creare un
modulo di nome
helpers.py con una libreria di nostre funzioni di utilità generale
# helpers.py
def display(message, is_warning=False):
if is_warning:
print(f"Warning!! {message}")
else:
print(message)
def somma(op1, op2):
return op1 + op2
in questo caso una funzione per stampare un log nella console e una per sommare due numeri.
La keyword return, nella funzione somma, viene usata per restituire il risultato della funzione.
Per usarlo in un altro file, dobbiamo prima
importarlo con
import nome_modulo# app.py
import helpers
# richiamo la funzione con la sintassi nome_modulo.nome_funzione
helpers.display("Funziona :-)", True)
# Warning!! Funziona :-)
print(helpers.somma(3, 5))
# 8
se non servono tutte le funzioni, è inutile importare tutto il
modulo, si può importare solo quella che serve con le keyword
from ... import ...# app.py
from helpers import display
# ho importato solo la funzione "display", la richiamo senza il prefisso del modulo
display("Ciao")
# Ciao
posso importare tutte le funzioni con l'asterisco
*
nel caso il
nome della funzione andasse in conflitto con un'altra già esistente, posso
rinominarla con la keyword
as# app.py
from helpers import display as prt
# non esiste la funzione "display" è diventata "prt"
prt("Ciao")
infine, nel caso avessi un conflitto a livello di
nome del modulo, posso rinominare tutto il
modulo# app.py
import helpers as mia_lib
print(mia_lib.somma(2,5))
# 7
Classi
Come dicevo nella
Parte 1,
Python è un linguaggio orientato agli oggetti.
La sintassi per definire una
classe è la seguente che fa uso della keyword
classclass NomeClasse:
proprieta_condivisa = valore
def __init__(self, parametro_1, parametro_2, ...):
# corpo del costruttore + proprietà
self.proprieta_1 = parametro_1
self.proprieta_2 = parametro_2
self.proprieta_N = parametro_N
def nome_metodo(self, parametro_a, parametro_b, ...):
# corpo del metodo
La convenzione per i nomi delle classi è quella di usare la notazione
CamelCaseGli aspetti su cui focalizzarsi sono:
- la classe si definisce tramite la keyword class
- il costruttore è una funzione con il nome riservato __init__
- il primo parametro del costruttore e degli altri metodi deve sempre essere self
- le proprietà si definiscono con la sintassi self.nome_proprieta e sono valide a livello di istanza (es.: self.proprieta_1)
- le proprietà definite a livello di classe son condivise tra tutte le istanze (es.: proprieta_condivisa), vanno modificate con la sintassi NomeClasse.proprieta
per istanziare una classe la sintassi è
variabile = NomeClasse(eventuali_parametri)
Da nota la mancanza di una keyword prima del nome della classe, ad esempio
new usata da
JavaScript o
C#Per capire come funziona, creiamo una classe di esempio
Macchinaclass Macchina:
# definisco una proprietà a livello di classe
modello = "Ford"
# quando istanzio la classe devo passare un identificativo e una velocità iniziale
def __init__(self, id_macchina, velocita):
self.velocita_attuale = velocita
self.id = id_macchina
# con questo metodo spengo il motore, velocità = 0
def spegni_motore(self):
self.velocita_attuale = 0
# con questo lo accendo ad un valore di default, velocità = 1
def accendi_motore(self):
self.velocita_attuale = 1
# con questo metodo posso impostare una velocità specifica
def imposta_velocita(self, velocita):
if velocita >= 0 and velocita < 10:
self.velocita_attuale = velocita
# questo metodo lo uso per cambiare la proprietà a livello di classe condivisa tra le istanze
def imposta_modello(self, modello):
# accesso alla proprietà a livello di classe
Macchina.modello = modello
# infine, un metodo che mi restituisce lo stato delle macchina
def info(self):
return f"Veicolo: {self.id}, velocità: {self.velocita_attuale}, modello: {Macchina.modello}"
istanziamole,
macchina_uno e
macchina_due'',' invocando metodi''' (il risultato è nei commenti)
# creo due macchine
macchina_uno = Macchina(1, 3)
macchina_due = Macchina(2, 1)
print("Start:", macchina_uno.info(), "|", macchina_due.info())
# Start: Veicolo: 1, velocità: 3, modello: Ford | Veicolo: 2, velocità: 1, modello: Ford
macchina_uno.spegni_motore()
print("Spegni 1 =>", macchina_uno.info(), "|", macchina_due.info())
# Spegni 1 => Veicolo: 1, velocità: 0, modello: Ford | Veicolo: 2, velocità: 1, modello: Ford
macchina_uno.accendi_motore()
print("Accendi 1 =>", macchina_uno.info(), "|", macchina_due.info())
# Accendi 1 => Veicolo: 1, velocità: 1, modello: Ford | Veicolo: 2, velocità: 1, modello: Ford
macchina_uno.imposta_velocita(7)
print("Imposta 1 =>", macchina_uno.info(), "|", macchina_due.info())
# Imposta 1 => Veicolo: 1, velocità: 7, modello: Ford | Veicolo: 2, velocità: 1, modello: Ford
macchina_due.imposta_velocita(9)
print("Imposta 2 =>", macchina_uno.info(), "|", macchina_due.info())
# Imposta 2 => Veicolo: 1, velocità: 7, modello: Ford | Veicolo: 2, velocità: 9, modello: Ford
macchina_due.imposta_modello("Fiat")
print("Modello 2 =>", macchina_uno.info(), "|", macchina_due.info())
# Modello 2 => Veicolo: 1, velocità: 7, modello: Fiat | Veicolo: 2, velocità: 9, modello: Fiat
Rivedi la
Parte 1 oppure prosegui con la
Parte 3