PRECEDENZA: not --> and --> or
if condizione1:
istruzioni eseguite se è vera la condizione1
elif condizione2:
istruzioni eseguite se è falsa condizione1 ed è vera condizione2
...
else:
istruzioni eseguite se tutte le condizioni sono false
## SOLUZIONI
True and not False or True and False or not True
# è come
(True and (not False)) or (True and False) or (not True)
# ovvero
(True and True) or False or False
# quindi
True or False or False
True
# Ricordiamo che True == 1 e False == 0
True+False/True+True*True-False
# è lo stesso che
1 + 0/1 + 1*1 - 0
# ovvero
1 + (0/1) + (1*1) - 0
# quindi
1 + 0.0 + 1 - 0 # notate il float
2.0
is_raining = True
got_umbrella = False
have_money = True
if not is_raining:
print('go out')
elif not got_umbrella and have_money:
print('buy umbrella and go out')
elif got_umbrella:
print('go out since got umbrella')
else:
print('stay home')
buy umbrella and go out
QUALI sono i nomi ammessi come identificatori in python?
Date SEMPRE un nome esplicativo alle funzioni, variabili, argomenti e classi
"Programma sempre come se il ragazzo che dovrà manutenere il tuo codice sia uno psicopatico violento che sa dove vivi." Martin Golding
E DOVE si trovano i nomi delle variabili, funzioni e classi?
Nei NAMESPACE (tabelle dei nomi)
Ce ne sono almeno 3, uno dentro l'altro:
Per usare una variabile/funzione si usa la sintassi nomemodulo.variabile oppure nomemodulo.funzione(argomenti)
Ogni volta che usate un identificatore, verrà cercato in ordine LEGB:
QUINDI: una variabile locale può NASCONDERE una variabile globale con lo stesso nome
### ESEMPIO
G = 42 # definisco una variabile globale
def funzione_senza_effetti_collaterali(valore):
'funzione che definisce G come variabile locale'
G = valore
print(G, 'dentro la funzione il valore di G è quello locale')
print(G, 'prima di chiamare la funzione')
funzione_senza_effetti_collaterali(555)
print(G, 'dopo aver chiamato la funzione il valore è quello globale')
42 prima di chiamare la funzione 555 dentro la funzione il valore di G è quello locale 42 dopo aver chiamato la funzione il valore è quello globale
def funzione_CON_effetti_collaterali(valore):
global G # voglio usare la variabile globale e non una nuova locale
G = valore
print(G, 'dentro la funzione la variabile G è quella globale')
print(G, 'prima di chiamare la funzione')
funzione_CON_effetti_collaterali(666)
print(G, 'dopo aver chiamato la funzione il valore è cambiato')
42 prima di chiamare la funzione 666 dentro la funzione la variabile G è quella globale 666 dopo aver chiamato la funzione il valore è cambiato
Negli HW le variabili GLOBALI (e gli attributi di classe) sono proibiti
Aggiungeremo un test che controlla che non usiate GLOBALI o attributi di classe
def nome_funzione(argomenti):
corpo della funzione
return risultato
NOTA:
None
Reminder: Le variabili locali nascono alla chiamata della funzione e scompaiono al suo completamento (return)
ATTENZIONE: gli argomenti sono variabili locali e potete modificarli
nomi = ['Paperino', 'Topolino', 'Minnie', 'Annabella', 'Gastone']
def togli_terza(parole):
"qui modifico la lista togliendo l'elemento a indice 2"
parole.pop(2) # modifico l'oggetto riferito
def sostituisci_tutte_non_distruttiva(parole):
"qui rimpiazzo nella variabile locale 'parole' una nuova lista"
parole = ['uno', 'due', 'tre'] # sostituisco il RIFERIMENTO
def sostituisci_tutte_distruttiva(parole):
"qui rimpiazzo nella variabile locale 'parole' una nuova lista"
parole[:] = ['uno', 'due', 'tre'] # sostituisco il RIFERIMENTO
print(nomi, '\tlista iniziale')
togli_terza(nomi)
print(nomi, '\t\tdopo aver tolto il 3° elemento')
sostituisci_tutte_non_distruttiva(nomi)
print(nomi, '\t\tdopo sostituisci_tutte_non_distruttiva')
sostituisci_tutte_distruttiva(nomi)
print(nomi, '\t\t\t\t\t\tdopo sostituisci_tutte_distruttiva')
['Paperino', 'Topolino', 'Minnie', 'Annabella', 'Gastone'] lista iniziale ['Paperino', 'Topolino', 'Annabella', 'Gastone'] dopo aver tolto il 3° elemento ['Paperino', 'Topolino', 'Annabella', 'Gastone'] dopo sostituisci_tutte_non_distruttiva ['uno', 'due', 'tre'] dopo sostituisci_tutte_distruttiva
Gli argomenti formali nella definizione della funzione possono essere
Nella chiamata mettete prima i valori attuali obbligatori,
poi gli opzionali per posizione oppure per nome
# supponiamo di voler concatenare 3 parole
# ad una lista variabile di altre parole
def concatena(obb1, obb2, opz3=42, opz4=None):
if not isinstance(opz3, str): # se il 3° arg NON è una stringa
opz3 = str(opz3) # lo trasformo in stringa
if opz4 is None: # se il 4° arg non c'è
opz4 = ['viva', 'Topolin'] # uso una lista di valori preconfezionata
return ' '.join([obb1, obb2, opz3] + opz4)
# gli argomenti opzionali sono sia posizionali che usabili per nome
concatena('Minni', 'Paperino', 'Paperoga', ['Ciccio'])
'Minni Paperino Paperoga Ciccio'
concatena('Minni', 'Paperino', 'Paperoga', opz4=['Pluto'])
'Minni Paperino Paperoga Pluto'
concatena('Minni', 'Paperino', opz4=['Ciccio'])
'Minni Paperino 42 Ciccio'
concatena('Minni', 'Paperino')
'Minni Paperino 42 viva Topolin'
# per passarli *fuori sequenza* va usato il nome
# in realtà potete anche passare tutti gli argomenti per nome
# in qualsiasi ordine
concatena(obb2='Minni', opz3='Paperino', obb1='Paperoga', opz4=['Ciccio'])
'Paperoga Minni Paperino Ciccio'
Python crea i valori di default nel momento della definizione della funzione (e non alla sua CHIAMATA)
Per cui i valori di default vengono riusati in tutte le chiamate
None
come default # mi aspetterei che ad ogni chiamata senza parametri
# X sia valorizzato con una nuova lista vuota
# ma non è così, la lista è creata una sola volta
# nel momento della definizione della funzione
def modifico_il_default(X=[]):
X.append(12) # modifico la lista X
return X
print( modifico_il_default()) # non passo nessun valore
print( modifico_il_default()) # non passo nessun valore
print( modifico_il_default()) # non passo nessun valore
# si vede come la lista X cambia mano a mano
[12] [12, 12] [12, 12, 12]
# implementazione CONSIGLIATA
def non_modifico_il_default(X=None):
# costruisco una nuova lista vuota per ogni chiamata senza parametri
if X is None:
X = []
X.append(12)
return X
print( non_modifico_il_default()) # non passo nessun valore
print( non_modifico_il_default()) # non passo nessun valore
print( non_modifico_il_default()) # non passo nessun valore
[12] [12] [12]
Negli HW gli argomenti con default mutabile saranno proibiti
Aggiungeremo un test che controlla che non usiate argomenti di default mutabili
# ESEMPIO
for X in [1, 2, 3, 4]: # ripeto 4 volte, con X che prende i valori 1, 2, 3, 4
print(X) # l'istruzione print
1 2 3 4
Nel corpo del ciclo posso usare
break # per uscire immediatamente dal ciclo
# e proseguire DOPO tutto il FOR
continue # per saltare al prossimo elemento del FOR
# senza completare il blocco indentato
# ESEMPIO
for X in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]:
if X % 2 == 0: continue # salto tutti i numeri pari
if X == 13: break # esco se incontro il valore 13
print(X)
else:
print('li ho stampati tutti')
1 3 5 7 9 li ho stampati tutti
while condizione_vera:
blocco di codice da ripetere
aggiornamento della condizione
else: # opzionale
blocco di codice eseguito se NON si esce
con break
# ESEMPIO
X = 0
while X < 20:
X += 1 # aggiorno la condizione
if X % 2 == 0: continue
if X == 27: break
print(X)
else:
print('li ho stampati tutti')
1 3 5 7 9 11 13 15 17 19 li ho stampati tutti
## range crea un oggetto che fornisce un nuovo valore ogni volta
## che gli viene richiesto dal for o da list
R = range(10)
for i in R:
print(i, end=' ')
R
list(R)
0 1 2 3 4 5 6 7 8 9
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[ 1, 2, 3, 4, 5, ]
indicizzate e modificabili( 1, 2, 3, 4, 5, )
indicizzate e immutabili{ 1, 2, 3, 4, 5, }
senza ripetizioni, NON indicizzati e mutabili{ 'a': 1, 'b': 2, 'c': 3, }
associazioni chiave unica -> valore, indicizzati sulle chiavi, modificabili# Esempio:
lista_valori = [2, 5, 7, 23, 45, 2, 7, 23, ]
tupla_valori = tuple(lista_valori) # converto la lista in tupla
set_valori = set(lista_valori) # converto la lista in set
dizionario = { 'a': 1, 'b': 2, 'c': 3, }
lista_valori, tupla_valori, set_valori, dizionario
([2, 5, 7, 23, 45, 2, 7, 23], (2, 5, 7, 23, 45, 2, 7, 23), {2, 5, 7, 23, 45}, {'a': 1, 'b': 2, 'c': 3})
## le liste sono indicizzate E **modificabili**
print(lista_valori[4])
lista_valori[5] = 666
print(lista_valori)
45 [2, 5, 7, 23, 45, 666, 7, 23]
## gli insiemi NON sono indicizzati e sono **modificabili**
print(set_valori)
print(set_valori.pop()) # estraggo un elemento a caso
set_valori.add(666) # aggiungo un elemento
print(set_valori)
set_valori[4] # ERRORE!!!
{2, 5, 7, 45, 23} 2 {5, 7, 45, 23, 666}
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) Cell In[24], line 6 4 set_valori.add(666) # aggiungo un elemento 5 print(set_valori) ----> 6 set_valori[4] # ERRORE!!! TypeError: 'set' object is not subscriptable
## le tuple sono indicizzate E **immutabili**
print(tupla_valori[4])
tupla_valori[5] = 666 ### se provo a modificarla ERRORE!
45
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) /var/tmp/ipykernel_706375/3966009171.py in <cell line: 3>() 1 ## le tuple sono indicizzate E **immutabili** 2 print(tupla_valori[4]) ----> 3 tupla_valori[5] = 666 ### ERRORE TypeError: 'tuple' object does not support item assignment
# i dizionari sono indicizzati dalle chiavi e **modificabili**
print(dizionario.keys()) # estraggo le chiavi -> oggetto
print(list(dizionario.keys())) # estraggo le chiavi -> lista
print(dizionario['a']) # stampo il valore associato ad 'a'
dizionario['paperino'] = 42 # aggiungo una coppia chiave -> valore
print(dizionario) # il dizionario è cambiato
#dizionario['pluto'] # se la chiave non c'è ERRORE!!!
print('pluto' in dizionario)
print(list(dizionario.values()))
{ 'uno':1, 'due':2, 'uno':11 }
dict_keys(['a', 'b', 'c', 'paperino']) ['a', 'b', 'c', 'paperino'] 1 {'a': 1, 'b': 2, 'c': 3, 'paperino': 42} False [1, 2, 3, 42]
{'uno': 11, 'due': 2}
# se voglio stampare gli elementi dei diversi contenitori
for elemento in lista_valori:
print(elemento, end=' ') # stampa seguita da spazio invece che '\n'
print('\t\tlista_valori')
2 5 7 23 45 2 7 23 lista_valori
for elemento in set_valori:
print(elemento, end=' ')
print('\t\tset_valori')
5 7 45 23 666 set_valori
for elemento in tupla_valori:
print(elemento, end=' ')
print('\t\ttupla_valori')
2 5 7 23 45 2 7 23 tupla_valori
# sfruttiamo gli assegnamenti multipli di Python
for chiave,elemento in dizionario.items(): # items produce le coppie
print(chiave,elemento)
dizionario.items()
a 1 b 2 c 3 paperino 42
dict_items([('a', 1), ('b', 2), ('c', 3), ('paperino', 42)])
#### come scandire un contenitore per valori
for elemento in lista_valori:
print(elemento, end=' ')
print()
2 5 7 23 45 2 7 23
#### come scandire un contenitore per indice
for i in range(len(lista_valori)):
print(i, lista_valori[i])
0 2 1 5 2 7 3 23 4 45 5 2 6 7 7 23
#### come scandire un contenitore per valori ma sapendone l'indice
for indice,elemento in enumerate(lista_valori):
print(indice, elemento)
list(enumerate(lista_valori))
0 2 1 5 2 7 3 23 4 45 5 2 6 7 7 23
[(0, 2), (1, 5), (2, 7), (3, 23), (4, 45), (5, 2), (6, 7), (7, 23)]
What if elimino elementi dalla lista MENTRE la sto scandendo?
Insomma, mi tiro via il tappeto da sotto i piedi!!!
lista_interi = [ 0, 11, 22, 33, 44, 55, 66, 77, 88, ]
for i in range(len(lista_interi)):
print(lista_interi[i])
if i == 3:
del lista_interi[i] # elimino l'elemento in posizione 3
0 11 22 33 55 66 77 88
--------------------------------------------------------------------------- IndexError Traceback (most recent call last) Cell In[35], line 3 1 lista_interi = [ 0, 11, 22, 33, 44, 55, 66, 77, 88, ] 2 for i in range(len(lista_interi)): ----> 3 print(lista_interi[i]) 4 if i == 3: 5 del lista_interi[i] # elimino l'elemento in posizione 3 IndexError: list index out of range
# scansione a rovescio
lista_interi = [ 11, 22, 33, 44, 55, 66, 77, 88, ]
for i in range(len(lista_interi)-1, -1, -1): # range con incremento negativo
print(lista_interi[i], end=' ')
if i == 3:
del lista_interi[i]
print()
print(lista_interi, 'lista_interi')
88 77 66 55 44 33 22 11 [11, 22, 33, 55, 66, 77, 88] lista_interi
# oppure costruisco una nuova lista
nuova_lista = []
lista_interi = [ 11, 22, 33, 44, 55, 66, 77, 88, ]
for i in range(len(lista_interi)):
if i != 3:
nuova_lista.append(lista_interi[i])
print(lista_interi[i], end=' ')
print()
print(lista_interi, 'lista_interi')
print(nuova_lista, 'nuova_lista')
11 22 33 55 66 77 88 [11, 22, 33, 44, 55, 66, 77, 88] lista_interi [11, 22, 33, 55, 66, 77, 88] nuova_lista
# Soluzione
lista_valori = list(range(10))
somma = 0
for i in range(10):
if i == len(lista_valori): # esco se sono finiti gli elementi
break
if i % 3 == 0: # per tutti i multipli di 3
somma += lista_valori[i] # li sommo
print(lista_valori[i])
del lista_valori[i] # e li elimino
print(somma)
print(lista_valori) # valori rimasti nella lista
# volevamo sommare 0 3 6 9 => 18
# e invece abbiamo sommato 0 4 8 => 12
0 4 8 12 [1, 2, 3, 5, 6, 7, 9]
Spesso dobbiamo controllare se un contenitore è vuoto o contiene elementi (oppure se una variabile è 0 o diversa da 0). Per semplificare gli if-then-else:
Ed inoltre
## Per controllarlo trasformiamo contenitori vuoti in booleani
bool([]), bool(tuple()), bool({}), bool(set())
(False, False, False, False)
bool([1]), bool((2,)), bool({3:'tre'}), bool({4})
(True, True, True, True)
def stampa_lista(valori):
# se la lista contiene almeno un elemento
if valori:
print(valori[0]) # stampo il primo
stampa_lista(valori[1:]) # stampo gli altri
dati = [1, 2, 3, 4, 5]
stampa_lista(dati)
1 2 3 4 5