Funzioni 1: introduzione¶

Riferimenti: SoftPython - funzioni 1

  • visualizza al meglio in
  • versione stampabile: clicca qua
  • per navigare nelle slide: premere Esc


Summer School Data Science 2023 - Modulo 1 informatica: Moodle

Docente: David Leoni david.leoni@unitn.it

Esercitatore: Luca Bosotti luca.bosotti@studenti.unitn.it

Tipi di funzioni (all'incirca...)¶

SIDE EFFECTS¶

SIDE EFFECTS - esempio¶

Riferimenti: SoftPyhon - funzioni

def mia_stampa():
    primo = input('Dammi un numero: ')
    secondo = input('Dammi un altro numero')
    x = int(primo)
    y = int(secondo)
    print(f'La somma è {x + y}')    

mia_stampa()

Guarda in Python Tutor¶

Per definire una funzione, possiamo usare la parola chiave def:

Ricordati i due punti : alla fine della riga !!

Side effects: funziona?¶

def altra_stampa(x,y):   
    print('Stampiamo!')
    print('La somma è %s' % (x + y))    

risultato = altra_stampa(3,5)
print('risultato:', risultato)
print(risultato + 4)
Stampiamo!
La somma è 8
risultato: None
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
/tmp/ipykernel_12038/1339349047.py in <module>
      6 risultato = altra_stampa(3,5)
      7 print('risultato:', risultato)
----> 8 print(risultato + 4)

TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'

SIDE EFFECTS: Ricapitoliamo¶

Una funzione ha side effects quando modifica l'ambiente

  • richiedere input manuale all'utente
  • leggere da file
  • illuminare i pixel sullo schermo con print
  • scrivere su file

RITORNA un valore¶

RITORNA un valore - esempio¶

In [2]:
 
Out[2]:
Python Tutor visualization

Per restituire un valore usabile fuori dalla funzione:

usa la parola chiave return seguita da una ESPRESSIONE

E se ci dimentichiamo il return ?¶

In [3]:
def mia_somma_sbagliata(x,y):
    x + y    

risultato = mia_somma_sbagliata(5,3)
In [4]:
risultato  # Jupyter non mostra niente!
In [5]:
print(risultato)  # forziamo stampa con print
None
In [6]:
 
Out[6]:
Python Tutor visualization
In [7]:
def mia_somma_corretta(x,y):
    return x + y  

risultato = mia_somma_corretta(5,3)
print(risultato)
8

RITORNA un valore - if¶

Se sono presenti condizionali nella funzione:

ricordarsi di mettere un return in tutti i rami

In [8]:
 
Out[8]:
Python Tutor visualization

MODIFICA - che vuol dire? 1/3¶

MODIFICA - che vuol dire? 2/3¶

MODIFICA - che vuol dire? 3/3¶

MODIFICA - Esempio¶

In [9]:
 
Out[9]:
Python Tutor visualization

MODIFICA - funziona?¶

def ordina(lista):
    """Prende in input una lista e la MODIFICA
       in modo che sia ordinata in-place
    """
    lista.sort()

vestiti = [60,40,50,30]  # taglie
mistero = ordina(vestiti)
print('mistero:', mistero)
mistero.append("Chissà...")
print(mistero)
mistero: None
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
/tmp/ipykernel_11086/4147884502.py in <module>
      8 mistero = ordina(vestiti)
      9 print('mistero:', mistero)
---> 10 mistero.append("Chissà...")
     11 print(mistero)

AttributeError: 'NoneType' object has no attribute 'append'

MODIFICA - Ricapitoliamo¶

Cambia dati in regioni di memoria esistenti

NON ritorna niente!

NON stampa niente!

NON crea nuove regioni di memoria

  • (o limita la creazione al minimo necessario)

MODIFICA e RITORNA INPUT 1/2¶

MODIFICA e RITORNA INPUT 2/2¶

MODIFICA e RITORNA INPUT - Esempio¶

In [10]:
 
Out[10]:
Python Tutor visualization

MODIFICA e RITORNA INPUT - Funziona? 1/2¶

In [11]:
def miracolo(tavolo):
    """MODIFICA la lista tavolo raddoppiando 
       tutti i suoi elementi,
       e infine RITORNA la lista di input
    """
    for i in range(len(tavolo)):
        tavolo[i] = tavolo[i] * 2
    return tavolo


pani_pesci = [7,5]
miracolo(pani_pesci).reverse()              
print("pani e pesci: ", pani_pesci)    
pani e pesci:  [10, 14]

MODIFICA e RITORNA INPUT - Funziona? 2/2¶

def miracolo(tavolo):
    """MODIFICA la lista tavolo raddoppiando 
       tutti i suoi elementi,
       e infine RITORNA la lista di input
    """
    for i in range(len(tavolo)):
        tavolo[i] = tavolo[i] * 2
    return tavolo


pani_pesci = [7,5]
miracolo(pani_pesci).reverse().append(16)
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
/tmp/ipykernel_5605/3100610722.py in <module>
----> 1 miracolo(pani_pesci).reverse().append(16)

AttributeError: 'NoneType' object has no attribute 'append'

MODIFICA e RITORNA INPUT: Ricapitoliamo¶

NON STAMPA niente!

NON crea nuove regioni di memoria (o limita la creazione al minimo necessario)

Nota: permette così il concatenamento di chiamate (detto chaining)

MODIFICA E RITORNA PARTE INPUT¶

In [12]:
 
Out[12]:
Python Tutor visualization

MODIFICA E RITORNA UNA PARTE: Ricapitoliamo¶

MODIFICA l’input e RITORNA una parte di esso

NON STAMPA niente!

Argomenti keyword¶

Si possono aggiungere argomenti di default alla fine:

In [13]:
def ripeti(stringa, volte=3):    
    return stringa * volte

Quando si chiama la funzione si può:

In [14]:
ripeti('Rotola')             # omettere l'argomento di default
Out[14]:
'RotolaRotolaRotola'
In [15]:
ripeti("Rotola", volte=7)    # specificarlo per nome
Out[15]:
'RotolaRotolaRotolaRotolaRotolaRotolaRotola'
In [16]:
ripeti("Rotola",  2)         # scriverlo direttamente
Out[16]:
'RotolaRotola'

Argomenti keyword mutabili 1/2¶

ATTENZIONE: NON mettere argomenti di defaut mutabili

Lo stesso oggetto verrà condiviso da tutte le invocazioni della funzione!

In [17]:
def tragedia(lista=[]):
    lista.append('occhio!')
    print(lista)
In [18]:
tragedia()  # prima invocazione: sembra a posto..
['occhio!']
In [19]:
tragedia()  # seconda invocazione: guai!
['occhio!', 'occhio!']

Argomenti keyword mutabili 2/2¶

In [20]:
def meglio(lista=None):
    if lista == None:
        lista = []
    lista.append('ok')
    print(lista)
        
meglio()  # prima invocazione
['ok']
In [21]:
meglio()  # seconda invocazione
['ok']

Variabili esterne¶

Da dentro una funzione, possiamo leggere variabili esterne:

In [22]:
 
Out[22]:
Python Tutor visualization

Variabili esterne: problemi¶

In [23]:
 
Out[23]:
Python Tutor visualization

Ma se proviamo a riassegnare, Python crea una NUOVA variabile!

Variabili esterne: global¶

Per evitare problemi, si può dichiarare la variabile esterna come global:

In [24]:
 
Out[24]:
Python Tutor visualization

NO global! Variabili esterne immutabili¶

Di solito è preferibile evitare global, come facciamo senza?

Consegna informale: Scrivi una funzione che dipinge muro di arancione

muro = "bianco"

def dipingi(   ):  # A. che parametri???        
    """ B. scrivi un testo il più preciso possibile per descrivere la funzione
    """
    # C. che corpo ????


# D. come chiamare??? 


print('Adesso il muro è', muro)

NO global! Variabili esterne mutabili¶

Di solito è preferibile evitare global, come facciamo senza?

  • Consegna informale: Scrivi una funzione che dipinge tutti i muri originali di arancione

  • Controlla di aver veramente cambiato la regione di memoria originale!!

muri = ["bianco", "azzurro", "bianco"]

def dipingi(   ):  # A. che parametri???    
    """ B. scrivi un testo il più preciso possibile per descrivere la funzione
    """    
    # C. che corpo ????


# D. come chiamare??? 


print('Adesso i muri sono', muri)

Funzioni lambda¶

Che cos'è una funzione? Un oggetto

Esempio: funzione len

In [25]:
len
Out[25]:
<function len(obj, /)>
In [26]:
len("rosa")         # invocazione
Out[26]:
4

Proviamo a creare una var mia_var che punta all'oggetto len:

In [27]:
mia_var = len

NOTA: non abbiamo aggiunto parametri a len!

Adesso possiamo usare mia_var esattamente come usiamo la funzione len:

In [28]:
mia_var("rosa")
Out[28]:
4

Funzioni lambda - esempio¶

Per creare una funzione 'al volo', usa la parola chiave lambda:

In [29]:
lambda x, y: x + y
Out[29]:
<function __main__.<lambda>(x, y)>
  • NON c'è il def
  • NON bisogna mettere return
  • DEVE stare su una riga sola

Una funzione lambda è un oggetto -> si può assegnare ad una variabile:

In [30]:
mia_somma = lambda x, y: x + y
In [31]:
mia_somma(3,5)
Out[31]:
8

funzioni lambda e def¶

Queste due scritture sono completamente equivalenti:

In [32]:
def mia_somma(x, y):
    return x + y
In [33]:
mia_somma = lambda x, y: x + y

Esempio: sorted e lambda¶

Aspettative di vita di animali:

In [34]:
animali = [('cane', 12), 
           ('gatto', 14), 
           ('pellicano', 30), 
           ('acquila', 25), 
           ('scoiattolo', 6)]

Come fare a ordinare per anni?

In [35]:
sorted(animali)   # SBAGLIATO
Out[35]:
[('acquila', 25),
 ('cane', 12),
 ('gatto', 14),
 ('pellicano', 30),
 ('scoiattolo', 6)]

Così è alfabetico...

Esempio: sorted key¶

Possiamo passare al parametro key una funzione che:

  • dato un elemento della lista...
  • ...produce un numero per ordinare

Scriviamo una lambda per estrarre aspettativa di vita:

In [36]:
sorted( animali, key=lambda tup: tup[1]  )
Out[36]:
[('scoiattolo', 6),
 ('cane', 12),
 ('gatto', 14),
 ('acquila', 25),
 ('pellicano', 30)]
In [37]: