Forum >> Principianti >> Funzioni e comando return

Pagina: 1

Salve, ho tre domande riguardanti l' esecuzione, o chiamata che dir si voglia, di questa semplice funzione:




def func(x):
print(x)
print("this line is printed")
return 3*x




1) nell' IDLE se chiamo la funzione dandogli come parametro 3, cioè digitando func(3), l' output è quello che mi aspetto:




3

this line is printed

9




2) sempre nell' IDLE se invece assengo ad una variabile 't' la chiamata di funzione func(3), cioèscrivo t=func(3) ottengo:




3

this line is printed





Questo output non arrivo a capirlo...°_o perchè IDLE esegue le due linee di codice ? Masoprattutto, perchè assegnando la chiamata di funzione alla variabile 't' luila esegue? Mica ho detto a Python di stampare a video la variabile t....non capisco..




3) se invece nell' IDLE scrivo print(t) ottengo come output 12, che ovviamente non capisco perchè....:(((






ciao roberto,

il fatto sta proprio nel tener presente il dove stai lavorando nel senso, se scrivi il codice nel file ".py" e poi lo vai ad eseguire è una cosa se invece lavori tanto per intenderci dove trovi i simboli ">>>" è tutt'altra cosa

in pratica per visualizzare il return nella shell / terminale / console comunque sia basta che digiti func(3) mentre nell'output devi inserire la funzione nel print(func(3)) come appunto hai fatto con la variabile t "print(t)"

in sostanza "print(func(3))" è la stessa cosa se fai "t=func(3)" e poi "print(t)"
Allora, non è una domanda ingenua, ed è una di quelle cose che in un modo o nell'altro i libri non spiegano mai benissimo secondo me (ok, Pensare da informatico non la spiega proprio, ma io parlavo dei libri fatti bene).


Segui il ragionamento. Quando sei nella shell di python, ogni volta che premi "invio" succedono cose diverse a seconda della riga che hai appena scritto. Il caso più facile è che tu abbia scritto una semplice espressione: in questo caso python 1) valuta l'espressione, 2) crea l'oggetto che rappresenta il risultato della valutazione, 3) sputa nello standard output la *rappresentazione* del valore di quell'oggetto (che è sempre una stringa, indipendentemente dall'oggetto), e infine 4) siccome l'oggetto-risultato non è collegato a nessuna variabile, allora viene distrutto dal garbage collector. Questo è quello che succede quando scrivi cose come:

>>> 2 + 3
5
>>> 'ciao ' + 'mondo'
'ciao mondo'
>>> (5 + 3) > 4
True
>>> 'mamma'
'mamma'
Che cosa è, *formalmente*, una espressione? Lasciamo perdere, è più complicato di quanto sembra. Diciamo che ci siamo capiti. Tutte quelle che ho scritto sopra sono espressioni. La rappresentazione di un oggetto, invece, è la stringa restituita dal metodo "__repr__" dell'oggetto: questo metodo per contratto ci deve sempre essere, e deve sempre restituire una stringa (perché deve poter finire dello standard output/error, nel caso).


Secondo caso: quando invece usi l'operatore di assegnamento "=" per assegnare una variabile, allora python fa una cosa diversa. Valuta l'espressione alla destra dell'operatore e crea l'oggetto-risultato come prima. Ma poi *non* distrugge l'oggetto, e invece lo mantiene in vita "collegandolo" al nome della variabile che hai scelto. Siccome l'oggetto non si perde, non c'è bisogno di scriverne la rappresentazione nello standard output. Se vuoi vedere che cosa "vale" l'oggetto, puoi sempre raggiungerlo attraverso la variabile collegata.


>>> a = 2 + 3
>>> b = 'ciao'
>>> c = 5 > 3
Naturalmente in una espressione può comparire il nome di una variabile impostata in precedenza. Le regole sono le stesse, che si tratti di una semplice espressione o di un assegnamento a una nuova variabile:

>>> d = a + 10 # questo e' un assegnamento...
>>> a * 2      # questa e' una espressione semplice...
10
>>> c          # un'altra espressione semplice...
True
Terzo caso: quando scrivi la definizione di una funzione ("def..."), questa si comporta più o meno come un assegnamento (un po' alla lontana, non prendermi alla lettera): in pratica stai assegnando a un nome non il risultato di una espressione, ma tutto un pezzo di codice da eseguire più tardi. Successivamente, quando chiami la funzione, python esegue il codice collegato e sputa fuori il risultato previsto dall'istruzione "return" della funzione. La chiamata di una funzione può avvenire in una semplice espressione, o in un assegnamento, e si applicano le stesse regole:

>>> def foo(x):   # definizione della funzione: collego del codice al nome "foo"
...     return x * 2
...
>>> foo(2)  # chiamo la funzione all'interno di una semplice espressione
4
>>> a = 5 + foo(3)  # chiamo la funzione all'interno di un assegnamento
>>> a
11
Nota invece che se non *chiami* la funzione (con le parentesi, cioè), ma ti limiti a inserirne il nome all'interno di una espressione, allora python non *esegue* il codice della funzione. Deve però valutare comunque l'espressione che hai scritto: quindi python si limita a chiamare il metodo __repr__ dell'oggetto-funzione (sì, anche le funzioni sono oggetti... in python tutto è un oggetto), che fornisce una stringa di descrizione standard dell'oggetto collegato al nome, e la sputa nello standard output:

>>> foo      # una semplice espressione da valutare...
<function foo at 0x000000E9C3B380D0>
Quarto caso. Idealmente una funzione è un oggetto con un punto di ingresso e un punto di uscita. Il punto di ingresso sono i parametri (zero o più) che la funzione accetta in input. Il punto di uscita è il risultato dell'espressione a destra dell'istruzione "return". Un dettaglio importante: tu *puoi* scrivere una funzione senza "return", ma in questo caso python fa comunque restituire alla funzione l'oggetto "None" per default. Quindi, scrivere una funzione senza "return" è equivalente a concludere la funzione con "return None". Il punto di uscita, insomma, c'è sempre. Puoi vedere una funzione come una scatola nera con solo una porta d'ingresso e una di uscita.Tuttavia, all'interno della funzione, è possibile fare delle cose che modificano l'ambiente circostante: aprire, chiudere e operare sui file; connettersi a un database; modificare variabili globali; eccetera. Questi interventi della funzione che "scappano" dalla scatola nera attraverso dei buchi che non sono i parametri di ingresso e il "return" di uscita, si chiamano "side-effect", effetti collaterali. Il più semplice dei side-effect, quello che il principiante impara subito, è ficcare una chiamata "print()" dentro la funzione. Questo è un side-effect perché scrive qualcosa nello standard output *separatamente* dai parametri d'ingresso e dal return di uscita.


>>> def foo(x):
...     print('io sono un side-effect')
...     return x * 2
Ora, i side-effect sono giustamente considerati "pericolosi": spesso non si possono evitare, ma non bisogna abusarne. Sono pericolosi perché introducono delle deviazioni dal flusso lineare del programma, sono difficili da testare, hanno effetti non-locali (e quindi spesso difficili da indovinare leggendo il codice). Ma tutto questo adesso importa poco. Quello che devi capire è che adesso, ogni volta che tu chiami "foo", python esegue il codice della funzione a partire dal parametro di ingresso e restituisce quanto previsto dal suo "return". Ma *inoltre* stampa anche una riga di testo nello standard output. Questo side-effect avviene sempre, in qualunque momento tu chiami la funzione. Ma a parte questo side-effect che si aggiunge al regolare comportamento della funzione, valgono le stesse regole che abbiamo già visto:


>>> foo(2)  # chiamo la funzione all'interno di una espressione semplice
io sono un side-effect
4
>>> foo(3) + 5  # un'altra espressione semplice
io sono un side-effect
11
>>> a = foo(2) + 4  # chiamo la funzione all'interno di un assegnamento
io sono un side-effect
>>> a
8
Ecco fatto. Se hai seguito fin qui, passo passo e con attenzione, sei in grado di capire il tuo problema originale e nel frattempo hai fatto qualche passo avanti nella comprensione di come funziona python. Mi chiedo davvero perché qualche dannato libro non possa semplicemente spiegare *questo*.







ciao Ric e grazie della spiegazione molto dettagliata che hai condiviso

volevo chiederti un chiarimento, hai detto che il print in quel contesto è un side-effect, quindi mi chiedevo se ci sono anche di altri tipi di side-effect e nel caso quali sono?

Grazie mille RicPol spiegazione davvero esaustiva, ed hai ragione, raramente ho trovato/si trova un video, un testo, un esempio che spieghi per bene la stretta correlazione tra il comando return e il blocco di codice dentro una funzione. Quello della scatola con ingresso ed uscita, che riporta in output quanto scritto a destra del return è il punto chiave della spiegazione.




Altro paio di maniche, ma forse è già trattato in un altro post, dove posso trovare una buona spiegazione solamente sul comando return? Intendo dire, non solo all' interno di una funzione, ma anche dentro a controlli del flusso, if, while o for per esempio. Ecco lì vorrei vedere degli esempi per comprendere bene questo comando, magari applicando in linea generale quello che hai spiegato per la funzione.




Grazie ancora

> mi chiedevo se ci sono anche di altri tipi di side-effect

Hai letto con attenzione quello che ho scritto, vedo.





> una buona spiegazione solamente sul comando return?


> non
solo all' interno di una funzione, ma anche dentro a controlli del
flusso,


> if, while o for per esempio.


Inizio della buona spiegazione.


"Return" si può usare solo all'interno delle funzioni.


https://docs.python.org/3/reference/simple_stmts.html#the-return-statement


Fine della buona spiegazione.


Pagina: 1



Esegui il login per scrivere una risposta.