# ---
# jupyter:
# jupytext:
# formats: ipynb,py:percent
# text_representation:
# extension: .py
# format_name: percent
# format_version: '1.3'
# jupytext_version: 1.14.0
# kernelspec:
# display_name: Python 3 (ipykernel)
# language: python
# name: python3
# ---
# %% [markdown] slideshow={"slide_type": "skip"} toc=true
#
Table of Contents
#
# %% [markdown] slideshow={"slide_type": "slide"}
# # Fondamenti di Programmazione
#
#
# **Andrea Sterbini**
#
# lezione 4 - 13 ottobre 2022
# %% [markdown] slideshow={"slide_type": "subslide"}
# ## Ancora funzioni
# ```python
# def nome_funzione(argomenti):
# corpo della funzione
# return risultato
# ```
# %% [markdown] slideshow={"slide_type": "fragment"}
# **NOTA:**
# - l'istruzione **return** può essere ovunque nel corpo della funzione,
# ne ferma l'esecuzione **fornendo al programma chiamante** (tornando) il valore indicato
# - se NON si esegue return la funzione torna il valore speciale **`None`**
# %% [markdown] slideshow={"slide_type": "subslide"}
# **Reminder:** Le variabili locali nascono alla chiamata della funzione e scompaiono al suo completamento (return)
#
# **ATTENZIONE:** gli argomenti sono **variabili locali** e potete modificarli
# - se si tratta di valori **immutabili** il programma chiamante
**NON se ne accorgerà**
# - se si tratta di valori **mutabili e ne modificate il contenuto** il programma chiamante **se ne accorgerà**
# - se invece sostituite il **riferimento** della variabile locale, il programma principale **NON se ne accorgerà**
# %% slideshow={"slide_type": "subslide"}
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(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)
togli_terza(nomi)
print(nomi)
sostituisci_tutte(nomi)
print(nomi)
sostituisci_tutte_distruttiva(nomi)
print(nomi)
# %% [markdown] slideshow={"slide_type": "slide"}
# ## Ancora funzioni
#
# Gli **argomenti formali** nella definizione della funzione possono essere
# - obbligatori, posizionali, (**sempre all'inizio**)
# - opzionali, con un valore di default da usare se il valore attuale non viene fornito (**sempre alla fine**)
# - PRIMA gli obbligatori POI gli opzionali con i loro default
#
# Nella chiamata mettete prima i **valori attuali obbligatori**,
poi gli **opzionali** per posizione oppure per nome
# %% slideshow={"slide_type": "subslide"}
# supponiamo di voler concatenare 3 parole
# ed 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 posizionali E ANCHE NO
concatena('Minni', 'Paperino', 'Paperoga', ['Ciccio'])
concatena('Minni', 'Paperino', 'Paperoga', opz4=['Pluto'])
concatena('Minni', 'Paperino', opz4=['Ciccio'])
concatena('Minni', 'Paperino')
# 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'])
# %% [markdown] slideshow={"slide_type": "slide"}
# ### Un errore molto difficile da notare: valori di default modificabili
# 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**
# %% [markdown] slideshow={"slide_type": "slide"}
# - se sono immutabili non c'è problema
(numeri, stringhe, None, tuple)
# - se sono mutabili **NON DOVETE CAMBIARLI**
altrimenti in altre chiamate saranno diversi!
# - se vi serve che siano modificati **USATE `None` come default**
e create il valore che vi serve come default SOLO a run-time
# %% slideshow={"slide_type": "subslide"}
# 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
# %% slideshow={"slide_type": "subslide"}
# implementazione corretta
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
# %% [markdown] slideshow={"slide_type": "slide"}
# # Cicli ed iterazione
# **Sintassi:**
# ```python
# # alla variabile viene assegnato il prox valore
# for variabile in sequenza:
# blocco di codice da ripetere per ogni valore
# else: # opzionale
# blocco che viene eseguito se si è usciti
# normalmente (senza break)
# ```
# %% [markdown] slideshow={"slide_type": "fragment"}
# Nel corpo del ciclo posso usare
# ```python
# break # per uscire immediatamente dal ciclo
# continue # per saltare al prossimo elemento
# # senza completare il blocco
# ```
# %% [markdown] slideshow={"slide_type": "subslide"}
# ## se NON sapete quante iterazioni fare
# ```python
# while condizione_vera:
# blocco di codice da ripetere
# aggiornamento della condizione
# else: # opzionale
# blocco di codice eseguito se NON si esce
# con break
# ```
# %% [markdown] slideshow={"slide_type": "slide"}
# ## Sequenze: oggetti che forniscono un elemento per volta
# #### La funzione **range**
# **range(fine)** | | **0, 1, 2, 3, 4.... (fine-1)**
# -----------------|--|-----------------------------
# **range(inizio, fine)** | | **inizio, inizio+1, .... , fine-1**
# **range(inizio, fine, incremento)** | | **inizio, inizio+incremento, ...., fine-1**
# %% slideshow={"slide_type": "fragment"}
## range crea un oggetto che fornisce un nuovo valore ogni volta
## che gli viene richiesto dal for
R = range(10)
for i in R:
print(i, end=' ')
R
list(R)
# %% [markdown] slideshow={"slide_type": "slide"}
# ## I contenitori:
# ### liste, tuple, dizionari, insiemi
# - **liste**: `[ 1, 2, 3, 4, 5, ]` indicizzate e modificabili
# - **tuple**: `( 1, 2, 3, 4, 5, )` indicizzate e immutabili
# - **insiemi**: `{ 1, 2, 3, 4, 5, }` senza ripetizioni, NON indicizzati e mutabili
# - **dizionari**: `{ 'a': 1, 'b': 2, 'c': 3, }` associazioni chiave unica -> valore, indicizzati sulle chiavi, modificabili
#
# %% slideshow={"slide_type": "subslide"}
# 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
# %% slideshow={"slide_type": "subslide"}
## le liste sono indicizzate E **modificabili**
print(lista_valori[4])
lista_valori[5] = 666
print(lista_valori)
# %% slideshow={"slide_type": "fragment"}
## 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!!!
# %% slideshow={"slide_type": "fragment"}
## le tuple sono indicizzate E **immutabili**
print(tupla_valori[4])
tupla_valori[5] = 666 ### se provo a modificarla ERRORE!
# %% slideshow={"slide_type": "subslide"}
# 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!!!
'pluto' in dizionario
list(dizionario.values())
{ 'uno':1, 'due':2, 'uno':11 }
# %% slideshow={"slide_type": "subslide"}
# 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')
for elemento in set_valori:
print(elemento, end=' ')
print('\t\tset_valori')
for elemento in tupla_valori:
print(elemento, end=' ')
print('\t\ttupla_valori')
for chiave,elemento in dizionario.items(): # items produce le coppie
print(chiave,elemento)
dizionario.items()
# %% slideshow={"slide_type": "subslide"}
#### come scandire un contenitore per valori
for elemento in lista_valori:
print(elemento, end=' ')
print()
# %% slideshow={"slide_type": "fragment"}
#### come scandire un contenitore per indice
for i in range(len(lista_valori)):
print(i, lista_valori[i])
# %% slideshow={"slide_type": "subslide"}
#### come scandire un contenitore per valori ma sapendone l'indice
print()
for indice,elemento in enumerate(lista_valori):
print(indice, elemento)
list(enumerate(lista_valori))
# %% [markdown] slideshow={"slide_type": "subslide"}
# ## E' "PROIBITO" modificare la lista
sulla quale si sta iterando?
# What if elimino elementi dalla lista che sto scandendo?
# - sono nella posizione 3,
# - elimino l'elemento,
# - il 4° si sposta in posizione 3
# - passo all'elemento in posizione 4
# - e **MI PERDO QUELLO CHE ERA IN POSIZIONE 4!!!**
# - e **HO ERRORE SULL'ULTIMO ELEMENTO!!!**
#
# Insomma, mi tiro via il tappeto da sotto i piedi!!!
# %% slideshow={"slide_type": "subslide"}
lista_interi = [ 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
# %% [markdown] slideshow={"slide_type": "subslide"}
# ## COME RISOLVERE?
#
# - **opzione 1**: scandisco la lista dalla fine all'inizio
# - in questo modo le modifiche spostano elementi GIA' ESAMINATI
# - ed inoltre gli indici esistono tutti (no IndexError)
# - **oppure**: costruisco una nuova lista
# - in questo modo non modifico la lista originale
# - **in genere**: itero su una lista SENZA MODIFICARLA (o s una sua copia), e mi costruisco altre strutture dati
# %% slideshow={"slide_type": "subslide"}
# 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')
# %% slideshow={"slide_type": "subslide"}
# 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')
# %% [markdown] slideshow={"slide_type": "subslide"}
# ## Momento Wooclap
# #### cosa stampa il programma ?
# 
# %% slideshow={"slide_type": "subslide"}
# 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
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
# %% [markdown] slideshow={"slide_type": "subslide"}
# ## Scorciatoie logiche per controllare i contenitori nei test if/then/else
#
# 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:
# - un contenitore vuoto vale come **False**
# - un contenitore con almeno un elemento vale **True**
#
# Ed inoltre
# - un valore 0 vale False
# - un valore diverso da zero vale True
# %% slideshow={"slide_type": "subslide"}
## Per controllarlo trasformiamo contenitori vuoti in booleani
bool([]), bool(tuple()), bool({}), bool(set())
# %% slideshow={"slide_type": "fragment"}
bool([1]), bool((2,)), bool({3:'tre'}), bool({4})
# %% slideshow={"slide_type": "fragment"}
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)