Jake 💥 rockz e oggi ci racconta di un caso che neanche Dirk Gently… questo.
Lo riscrivo con una piccola modifica (l’istruzione print
per vedere il risultato), file e0.py
:
def g(x):
def f():
x += 1
f()
print(g(0))
Err! 😡, e come dice Jake: That was a fun one to explain 😀. Dico subito che da solo non ci sono arrivato; poi mi sono detto che dovevo pensarci ma…
Provo a fare come avrei dovuto. Invece di operare sulla variabile (globale) x
nella f()
ritorno il valore di x
modificato, così (file e1.py
):
def g(x):
def f():
return x + 1
f()
print(g(0))
OOPS! per Python è OK ma non per me, correggo (e2.py
):
def g(x):
def f():
return x + 1
return f()
print(g(0))
Chiaro? Torno al tweet, nel thread c’è la dritta, il link a –indovina– la documentazione ufficiale di Python, qui: Why am I getting an UnboundLocalError when the variable has a value?
La spiegazione è diversa (più esaustiva) della mia, in sintesi:
This is because when you make an assignment to a variable in a scope, that variable becomes local to that scope and shadows any similarly named variable in the outer scope. Since the last statement in foo assigns a new value to
x
, the compiler recognizes it as a local variable. Consequently when the earlierprint(x)
attempts to print the uninitialized local variable and an error results.
Logico. E con certi linguaggi –nuovi, di moda– le variabili, quando ci sono, sono quelle, restano tali, niente modifiche. Per esempio Haskell –lasciato come esercizio. È da tanto che volevo dire questa frase 😊.
Invece in un’altra famiglia di linguaggi –sexy, i miei preferiti– non serve scrivere “return
“, la funzione è un valore (file e3.rkt
):
(define (g x)
(define (f)
(+ 1 x))
(f))
(displayln (g 0))
Ma nessun lisper userebbe la funzione f()
, viene chiamata una volta sola, per questo c’è lambda
(e4.rkt
):
(define (g x)
((lambda (t) (+ 1 t)) x))
(displayln (g 0))
Non mi piace, inutilmente contorta, basta (e5.rkt
):
(define (g x)
(+ 1 x))
(displayln (g 0))
Dove ho semplificato (troppo? f
e g
in un programma reale conterranno altre istruzioni) il codice. Ma era ridondante, lambda
si usa quando serve.
Poi ci ho ripensato ancora (tutta la notte) e sono giunto a una conclusione che forse vale solo per me ma penso che il codice del tweet sia sbagliato come progettazione. Se x
dev’essere globale si deve dichiarare, così (e6.py
):
def g(x):
def f():
global x
x += 1
f()
# main
x = 0
g(0)
print(x)
Ovviamente il parametro in g()
è inutile e dannoso come si scoprirà nel debug della prima revisione (e7.py
):
def g(x):
def f():
global x
x += 1
f()
# main
x = 0
g(42) # <- ====
print(x)
⭕