Archivi Categorie: Python

L’istruzione global in Python

Aaron Meurer twitta un quiz, questo. Sembra facile, anzi troppo e essendo un quiz c’è chi –correttamente– risponde senza eseguire il codice. La maggioranza sbaglia (55%).

Adesso non vi dico come ho votato ma devo confessare che avevo già affrontato un caso simile (c’era di mezzo il Basic, non sono riuscito a ritrovarlo, peccato). Ma mi va di ravanare l’argomento. Intanto la versione originale:

# aam.py

a = 1

def maybe_inc_a(inc):
  if inc:
    global a
  a += 1

maybe_inc_a(False)
print(a

OK, cioè no, inc è False quindi quindi il blocco if non viene eseguito, provo a commentarlo ed ecco:

# aam-c.py

a = 1

def maybe_inc_a(inc):
  # if inc:
  #   global a
  a += 1

maybe_inc_a(False)
print(a)

Errore, la soluzione la da Aaron (dopo un paio di giorni):

The global statement applies to the entire function regardless where it is.

E da anche un suggerimento:

The best practice is to put global declarations at the top of the function or right before the variable is first used.

Io che vengo dal Fortran sono per il top, chi è avvezzo a C/C++ preferisce molto probabilmente l’altra posizione (for int i...).

Tratta poi altri casi ma mi basta questo. Intanto, ripensandoci c’è un comportamento un po’ particolare, cosa succede con questo codice?

# aam-g.py

st = 'ciao'
a = 1

def maybe_inc_a(inc):
  print(st)
  print(a)

maybe_inc_a(False)

Ovviamente non è possibile modificare le variabili.

In questi casi diventa importante la documentazione. Cerco per global ed ecco:

The global statement is a declaration which holds for the entire current code block. It means that the listed identifiers are to be interpreted as globals. It would be impossible to assign to a global variable without global, although free variables may refer to globals without being declared global.

Names listed in a global statement must not be used in the same code block textually preceding that global statement.

Names listed in a global statement must not be defined as formal parameters or in a for loop control target, class definition, function definition, import statement, or variable annotation.

E subito sotto segue nonlocal, mai usato ma mai dire mai (uh! l’ho detto 3 volte in questa frase 😊).

In conclusione non ho detto niente più di Aaron ma metti che i miei amici pythonisti –colpevolmente– non lo seguano su Twitter…

Annunci

Python, le liste e i cicli for (e non riesco mai a ricordarmi)

Yep! proprio così. Ci sono delle cose che anche se ti sono già capitate (a me più volte) e finanche se te l’hanno già detto (a me più volte) non riesci a ricordartele. O capita solo a me? Oggi voglio scrivere di una di queste così me ne ricorderò. O se me lo faranno notare quando mi capiterà potrò dire “lo so; l’ho anche scritto, qui!”.

Non so voi ma alle volte la bontà di un’elaborazione (un run) viene valutata da un valore finale o la forma di una linea (o un punto) su un grafico. O sono solo io? A volte il valore finale non cambia al cambiare dei dati di input o il grafico è subito esatto o sempre sbagliato… Ecco parlo di uno di quei casi.

È una cosa semplice, la sanno tutti (anch’io) e nell’esempio qui sotto si vede subito dov’è il bug ma in un programma vero, scritto come al solito non è immediatamente evidente e bisogna andarlo a stanare. E (o capita solo a me?) i propri errori sono quelli che sono i più nascosti. Ma basta con l’intro giustificativa inizio.

Uno script se non limitato a poche righe sarà organizzato in funzioni, che possono poi essere contenute in classi. Mi occuperò solo delle funzioni, ecco quad() una funzione elementare per fare il quadrato di un numero:

def quad(n):
  return n * n

Funziona proprio come ci si aspetta (uso la REPL):

$ python3 -q
>>> def quad(n):
...   return n * n
...
>>> i = 5
>>> q = quad(i)
>>> q
25
>>> i
5

quad(), essendo Python un tipper ducko fa più di quel che si pensa:

>>> type(i)
<class 'int'>
>>> type(q)
<class 'int'>
>>> f = 4.2
>>> r = quad(f)
>>> r
17.64
>>> type(f)
<class 'float'>
>>> type(r)
<class 'float'>

In ogni caso i parametri (i e f) vengono passati per valore. Ecco una versione ridondante, con print di debug; tipo di istruzioni che possono capitare se la funzione è complessa e si è pasticcioni:

def quadp(n):
  n *= n
  print("dentro", n)
  return n

Vediamola in azione:

>>> n = 8
>>> s = quadp(n)
dentro 64
>>> s
64
>>> n
8

OK! una cosa simile capita con le stringhe:

>>> def up(st):
...   return st.upper()
...
>>> st = 'ciao!'
>>> us = up(st)
>>> st
'ciao!'
>>> us
'CIAO!'

ho barato: ho applicato a st il metodo upper() che non la modifica ma ne crea una copia. Se modifico st nella funzione ottengo:

>>> def upm(st):
...   st = st.upper()
...   print("dentro", st)
...   return st
...
>>> st
'ciao!'
>>> us = upm(st)
dentro CIAO!
>>> us
'CIAO'
>>> st
'ciao!'

Tutto come previsto.

E le liste? In Python le liste vengono usate (anche?) al posto degli arrays; e funzionano bene.

Ecco lsquad(), restituisce la lista dei quadrati degli elementi della lista passata.

>>> def lsquad(ls):
...   ls = [ls[n] * ls[n] for n in range (len(ls))]
...   return ls
...
>>> lorig = [1, 2, 3, 4]
>>> lq = lsquad(lorig)
>>> lq
[1, 4, 9, 16]
>>> lorig
[1, 2, 3, 4]

OK, ma io che sono vecchio, più vecchio della list comprehension a  volte  spesso scrivo così:

>>> def lsfq(ls):
...   for n in range(len(ls)):
...     ls[n] = ls[n] * ls[n]
...   return ls
...
>>> lorig
[1, 2, 3, 4]
>>> lq = lsfq(lorig)
>>> lq
[1, 4, 9, 16]
>>> lorig
[1, 4, 9, 16]

OOPS! con il ciclo for la stringa passata è stata aggiornata!

E chiamate successive producono questo effetto:

>>> lq = lsfq(lorig)
>>> lorig
[1, 16, 81, 256]
>>> lq = lsfq(lorig)
>>> lorig
[1, 256, 6561, 65536]

Mi suggeriscono (tra le altre cose) questa variante, con costruzione di una nuova lista (rl):

def lstq(ls):
  rl = []
  for n in ls:
    rl.append(n * n)
  return rl

È OK ma sono sempre per la list comprehension. Forse solo questione di gusti.
CMQ, panico? No, c’è tutto scritto nella documentazione. E adesso anche qui.

E poi, se vi ricordate anche il C… (quella cosa del puntatore all’elemento iniziale dell’array, o ricordo male?) 😉

Oh! post è già stato aggiornato (due volte) in bozza prima ancora di essere pubblicato nel blog. E chissà se adesso è OK.

👽

Un paio di cose OK di Python

Attualmente sono alle prese con un programmino scritto in Python sviluppato con un giovane nerd in remoto, 15 km. Funziona. E stanno capitando altre due cose che forse dovrei aver dato per scontato.

L’analisi e lo schema iniziale sono stati molto ampliati strada facendo; poi sono stati rivisti e aggiustati più volte. Ma mai stravolti e questo lo considero abbastanza normale.

Non so voi ma io vivo circondato da windowsiani duri e puri. Sempre in modo “chissene 🦎”. Tra le specifiche c’era che il programma era solo per Linux 😁 Poi… adesso vi conto.

In realtà lo sapevo, credo di averlo detto anche in questo blog (ma non mi va di cercarlo). Sì, è saltato fuori che “non si sa mai; e se poi…” insomma Windows 😐

Non l’ho fatto io ma lo riporto, è una caratteristica positiva di Python:

>>> import platform
>>> platform.platform()
'Linux-4.13.0-36-generic-x86_64-with-Ubuntu-17.10-artful'

OK, il più è fatto, ecco:

>>> pl=platform.platform()
>>> pl.startswith('Linux')
True

Le istruzioni legate all’OS sono pochissime. Ancora meno si hai cura di evitare pathnames letterali (ma non sempre è possibile). Ci sono poi alcune funzioni specifiche di un OS particolare; da evitare e ampiamente evitabili.

Post troppo corto? Allora aggiungo un’altra roba, questa scoperta solo ora (a me nessuno dice mai niente). È un problema dei dizionari, i dicts.
Il dict funziona perfettamente tranne che in un caso, improbabile ma c’è Murphy: i dati non sono sortati e se vuoi stamparlo su carta e lo vuoi ordinato come fai? Semplice:

>>> from operator import itemgetter
>>> anim = {'gatto' : 'cat', 'cane' : 'dog', 'maiale' : 'pig', 'mucca' : 'cow'}
>>> type(anim)
<class 'dict'>
>>> s_anim = sorted(anim.items(), key=itemgetter(0))
>>> type(s_anim)
<class 'list'>
>>> k_anim = dict(s_anim)
>>> type(k_anim)
<class 'dict'>
>>> k_anim
{'cane': 'dog', 'gatto': 'cat', 'maiale': 'pig', 'mucca': 'cow'}
>>> l_anim = sorted(anim.items(), key=itemgetter(1))
>>> l_anim
[('gatto', 'cat'), ('mucca', 'cow'), ('cane', 'dog'), ('maiale', 'pig')]

Semplice vero? si trasforma il dict in lista di tuple e questa è ordinabile con sorted(). Via itmgetter() posso selezionare il sort sia per key che value. Infine con dict ricavo il dizionario dalla lista.

Sì lo so che questo lo sanno tutti. Tranne me. A me nessuno dice mai niente, nada, zilch 😡 (auto-cit.).

👽

Il mio linguaggio favorito

Continuo a essere alla ricerca di tutorial di Haskell, per adesso senza successo.
Cioè ce ne sono tanti ma nessuno è perfetto (cit.). In particolare quando arrivi –se, non tutti ci arrivano– alle monadi risulta che gli esempi esplicativi –indispensabili, a quel punto mi sono perso– don’t run! 😡 C’è qualche funzione deprecata da così tanto tempo da essere stata rimossa o è il modulo che è cambiato, stravolto. Mi viene da fare un paragone fazioso quanto mai ma è la mia storia: quando io ho cominciato venivo katsiato perché il mio codice era diverso da quello dei wheels (OK, nessuno usava questo termine, e chissà se si usa ancora) e poi quella smania del nuovo, il 77. Ebbene sono pochissime le istruzioni che il compilatore rifiuta, anche per il IV (e forse anche prima). Certo il ciclo DO con contatore REAL ma si sapeva che era un’idea balzana; ENCODE e DECODE avevano senso finché mancavano le stringhe. Certo tanta roba nuova, tutta in meglio. Ma tutto compatibile con l’antico. Con Haskell no.

Ma per adesso non dispero, continuo a cercare, anzi ho una pista… 😉

Per intanto faccio cose, vedo gente (cit.) anche se quasi esclusivamente ontehtoobz. Ecco che Twitter mi cinguetta Good programmers don’t have a favorite language psssst basically all programmers have a favorite language. Ottimo tutto il thread.

Non è l’unico, ecco Alexis, lexi_lambda, con uno e due.

Il mio linguaggio preferito? È cambiato nel tempo, dal Fortran (era l’unico che c’era) al Pascal (e derivati, roba di Borland sotto DOS e Windows) a Python e Lisp (vari, in particolare Racket, che a rigore è Scheme). Da non dimenticare i linguaggi di scripting Unix|Linux: shell, AWK, …

Poi ci sarebbero quelli nuovi, ce ne sono di davvero sexy, ho visto Go, Rust, Julia e altri ancora. Fossi giovane mi butterei su Rust, penso però debba ancora crescere. Sono rimasto affascinato da Julia che deve ancora maturare. E lo vedo come alternativo a Python. Quindi…

Oggi Python –è lento mi dicono– è il mio linguaggio.

So benissimo che mancano diversi attori principali: Basic (una volta, usato ma mai considerato mio, probabilmente sbagliando), Java e JavaScript (quest’ultimo vince alla grande in popolarità, non solo sul web), C++ troppo –come dire– quasi come il Common Lisp, anzi di più. Altri ancora usati solo i certi ambiti come MATLAB|Octave.

In questi giorni ho scritto roba in Python, uno script inizialmente previsto in Bash (ehi! per una volta niente compatibilità con Windows) ma poi cresciuto troppo. Beh, viene tutto spontaneo, anche le funzioni meno probabili come la sys.path.insert(). No, non ne ricordavo|conoscevo il nome. Ma c’è nella documentazione. Come c’è la documentazione per i millemila packages che vengono sfornati ogni giorno. Per fortuna quasi tutti evitabili 😉

Essendo il linguaggio molto conosciuto si riesce a lavorare a quattro mani, in remoto (nel mio caso 15 km) dopo aver impostato lo schema (che ha poi subito diverse revisioni).

Ma devo continuare a cercare, voglio diventare funzionale puro. Maybe 😉

👽

Python – cose dimenticate (da me)

Sapete la memoria –la mia. E anche se Python (versione 3.x) è uno dei miei linguaggi di programmazione preferiti capita che perdo ore per non ricordare cose che sapevo, che ho usato tante volte e –adesso vi conto.

Copiare liste
Non è come mi è venuto da fare, ecco la versione che viene in mente al volo, senza stare lì a pensarci su. Identica per le versioni 2 e 3, cambia solo print che da parola chiave è diventato funzione.

copy2.py

L = [1, 2, 3]
M = L
print L
print M

print "\nmodifico L"
L[1] = 10
print L
print M

copy3.py

L = [1, 2, 3]
M = L
print(L)
print(M)

print("\nmodifico L")
L[1] = 10
print(L)
print(M)

Uh! correggo subito:

copy3in.py

L = [1, 2, 3]
M = L
print(L)
print(M)

print("\nmodifico L")
L.append(4)
print(L)
print(M)

print("\ncambio L")
L = [10, 20, 30, 40]
print(L)
print(M)

print("\ncome copiare")
L = [1, 2, 3]
M = list(L)
print(L)
print(M)

print("\ned ecco")
L[1] = 0
print(L)
print(M)

Intanto anche append() e funzioni similari funzionano come la versione niubba. Se invece cambio la lista l’equivalenza con la vecchia versione viene persa.

Ma il sistema vero per copiare è usare la funzione list.

Tutte cose che sapevo (OK, liberi di non credermi ma davvero, avevo già affrontato questo in passato).

Chiamata a funzione
Un’altra cosa dimenticata mi è capitata nel debug (che è ancora in corso). Mentre per le liste rimando alla documentazione quest’ultima è logica e risaputa da tutti. La so anch’io ma sapete com’è –la memoria.

func.py

def f(x):
  return x * x

# main
print(f(3))

g = f

print(g(5))

Notare i nomi economici, sto vedendo Haskell e sono stato contagiato. Notare come si può copiare la funzione f chiamandola g. Anzi le due sono la stessa funzione; cambia solo il nome. Quasi come in Haskell, dove però si esagera.

Ma tornando al dunque ho scoperto che c’è exit anche senza dover importarla da sys. Ecco la prima versione:

exit.py

print("prima di exit")
exit
print("dopo exit")

non funziona, ovviamente perché non è vista come una chiamata alla funzione con quel nome, mancano le parentesi, roba che fin dai tempi del C 😐

exit-f.py

print("prima di exit")
exit()
print("dopo exit")

Sì lo so, colpa mia, devo stare più attento. E consultare il manuale (online). E, dopo aver progettato con carta matita e gomma, controllare ogni singola parte (classe, funzione, sezione di codice) 😐

Bon, torno al debug, ancora non funziona 😡

🤩

Python o Bash per gli scripts?

Se devi ripetere tre (numero che può variare ma comunque non molto più grande di 3) volte la stessa sequenza di comandi conviene fare uno script. Nel mondo Linux (e Unix prima ancora) esistono diversi linguaggi di scripting, si può scegliere volta per volta il più adatto. Un caso simile è capitato proprio ieri, adesso racconto.

Mi serve un contatore per memorizzare successivamente log di elaborazioni. Ecco lo script Bash che ho prodotto:

x-num

#/bin/bash

FileName="$HOME/dep/elab-num"

if [ -e "$FileName" ]; then
  NUM=`cat $FileName`
else
  NUM=0
fi

NUM=$((NUM + 1))
echo $NUM > $FileName

echo $NUM # qui vanno i comandi veri

Il numero dell’ultima chiamata a x-num è memorizzato nel file ~/dep/elab-num. Lo script legge questo numero, lo incrementa di uno, aggiorna il file scrivendo il numero aggiornato e, in questo esempio, scrive sul terminale questo stesso numero. Nella versione vera qui vengono i comandi di elaborazione dei dati. Qualora il file di log non esista il numero dell’elaborazione viene assunto 0; quindi funziona anche in questo caso con indice 1.

Provo a testarlo:

OK, funziona.
Qui devo raccontare una cosa personale, mi è già capitata più volte, continua a capitare, anche ieri… Nello script non si può usare il carattwere ~ per indicare la home, bisogna usare $HOME, cosa che mi dimentico invariabilmente. Adesso che l’ho scritta chissà…

Ieri ero con un amico che è uscito con la solita domanda: “E per Windows come si fa?”

Bella domanda, io uso Python. E questo è l’equivalente Python del precedente

py-num.py

#!/usr/bin/python3

import os

home = os.getenv('HOME')

FileName = os.path.join(home, 'dep/elab-num')

if os.path.exists(FileName):
  with open (FileName, 'r') as f:
    num = int(f.readline())
else:
    num = 0

num += 1
with open (FileName, 'w') as f:
  print(num, file=f)

print(num)

Cancello il file del contatore per essere nelle stesse condizioni di prima ed ecco:

OK. I due scripts fanno esattamente la stessa cosa, possono essere usati in modo intecambiabile (con Linux).

Sono entrambi molto piccoli:

Quale preferire? Se è solo per me io sceglierei la versione Bash, ma sono gusti, l’abitudine (fin dagli anni ’80). La versione Python è però, previa modifica del nome del file, funzionante anche su Windows.

Probabilmente la variabile d’ambiente corrispodente a HOME credo sia HOMEPATH ma chi conosco opera differentemente impostando FileName = "c:/dep/elab-num.txt". Sì meglio specificare l’estensione, anche se di solito non si vede.

“Sai, queste cose si possono fare anche in Basic” (cit.). Certo 😋

🤩

È vero guarda qua: anche loro!

Non so se conoscete Sandro 💥, quello di Quantizzando 💥? Dovreste.
Non perché lui racconti di roba di ‘puters ma rockz! 💥 e poi oggi ci dice qualcosa dei linguaggi di programmazione che è proprio quello che volevo sentir dire.

Si può partire da questo cinguettio: Come utilizzare le pulsar per osservare le onde gravitazionali generate subito dopo il Big Bang che vi indica il post sul blog di cui in oggetto.

Il post non parla di ‘puters e linguaggi per ‘puters ma ci dice (in fondo, ma leggetelo tutto il post):

Ultima cosa davvero molto importante: i dati e i codici usati dal team di NANOGrav sono pubblici.

Ah! qui volevo arrivare:
Indovinate il linguaggio? Sì! 💥
Indovinate che moduli usano? Sì! 💥

Proprio come dice sempre Jake (è astro 💥 anche lui) e, si parva licet, me too 😋

🤩

Leggere e scrivere dizionari (e JSON)

Forse ne ho già parlato in passato (ma la memoria…) e non è nemmeno una cosa tanto interessante ma capita e allora ecco 😊

Il dictionary è un tipo che può tornare comodo, spesso. Ci sono diversi modi per creane; copio dalla documentazione:

a = dict(one=1, two=2, three=3)
b = {'one': 1, 'two': 2, 'three': 3}
c = dict(zip(['one', 'two', 'three'], [1, 2, 3]))
d = dict([('two', 2), ('one', 1), ('three', 3)])
e = dict({'three': 3, 'one': 1, 'two': 2})
a == b == c == d == e
True

Spesso nella pratica succede che la costruzione avviene più gradualmente:

d0.py

dic = {}

dic.update({1:'foo'})
k = 3
v = 'baz'
dic.update({k:v})
k = 'due'
v = 'bar'
dic.update({k:v})

print('dic =', dic)      #1

dic.update({5:'cinque'})
print('dic =', dic)      #2

dic.pop(5)               #3
print('dic =', dic)

Autoevidente ma comunque lo creo vuoto, lo popolo e visaulizzo (#1). Aggiungo un altro elemento (#2), che poi cancello (#3).

Questo è semplice, sarebbe possibile crearlo ogni volta, probabilmente con uno o più cicli leggendo i dati da file.

Ma se il dizionario, costruito in tempi successivi, supera una certa dimensione conviene scriverlo su file e poi rileggerlo quando serve.

Anticamente (non so se c’è anche su questo blog) usavo istruzioni molto semplici.
Per memorizzarlo su file:

d1.py

dic = {1: 'foo', 3: 'baz', 'due': 'bar'} #1
st = str(dic)                            #2
with open('dic', 'w') as f:
    f.write(st)

il dizionario dic in #1 è quello precedentemente costruito. Per scriverlo su file devo conventirlo in stringa (#2).

Successivamente posso leggerlo:

d2.py

with open('dic', 'r') as f:
    st = f.read()            #1

dic = eval(st)               #2
print(dic)
print(type(dic))             #3

lo leggo come stringa (#1) che valuto con eval() (#2) ottenendo quanto voluto (#3).

OK, funziona. Ma si può fare in modo più canonico (Python è un linguaggio ancora in evoluzione, nel senso che si aggiungono moduli a un ritmo sconvolgente).

d3.py

import json                  #1

with open('jdic', 'r') as f: #2
    try:
        dic = json.load(f)
    except ValueError:
        dic = {}

print(dic)

Sì, esiste il modulo json (#1) che fa quello che ci serve. Con un paio di avvertenze:

  • sia le chiavi che i valori devono essere stringhe;
  • occorre usare " come delimitatore di stringa; ' da errore.

Questo è il contenuto del file jdic

La scrittura su file del dizionario via json peraltro non è soggetta alle condizioni necessarie per la lettura; la conversione a stringa di chiavi e valori che si rendessero necessarie è trasparente:

d4.py

import json

dic = {1: 'foo', 3: 'baz', 'due': 'bar'} #1

with open('jdicw', 'w') as f: #2
    json.dump(dic, f)

Il formato JSON è molto popolare, Python si adegua, esiste json.tool che deve essere usato nel terminale:

🤩

Non sempre i linguaggi sono uguali

Altro post su cose viste aggiornando script. Non so se interessa ma per me che sono niubbo… 😉

Questa è la versione minima di un codice che non fa quello che dovrebbe fare:

manca-0.py

def pari(n):
    print(n, "è pari")

def dispari(n):
    print(n, "è dispari")

def valeDieci():
    print('Dieci')

def p_d(n):
    ok = False
    if n % 2 == 0:
        ok = True
        pari(n)
    elif n % 2 == 1:
        dispari(n)
    elif ok and (n == 10):
        vale_dieci()

# main

p_d(5)
p_d(8)
p_d(10)

Come si vede la condizione ok and (n == 10) non viene eseguita anche quando il valore del parametro n è quello giusto. Ovvio ok è False, come settato all’inizio della funzione.

Probabilmente nello scrivere il codice si è fatta confusione tra leistruzioni if e switch/case. Quest’ultima è presente in tanti linguaggi (qui, qui e qui). A volte con nome diverso, p.es. case nel Basic, Delphi, Lisp (cond). Ma in questi linguaggi viene eseguito il primo caso vero, e si esce, è una variante di if/else if/.../else.

In Python manca, anche se c’è un modo semplice per costruirla, nella versione non-C.

La differenza fondamentale è che l’if non esamina tutti i casi (elif e else in Python) ma esce appena ne trova un caso vero. Quindi nel nostro caso la condizione eseguita è n % 2 == 0 cioè n è pari.

La correzione è immediata: suddividere l’if in due istruzioni:

manca-1.py

def pari(n):
    print(n, "è pari")

def dispari(n):
    print(n, "è dispari")

def valeDieci():
    print('Dieci')

def p_d(n):
    ok = False
    if n % 2 == 0:
        ok = True
        pari(n)
    elif n % 2 == 1:
        dispari(n)

    if ok and (n == 10):
        vale_dieci()

# main

p_d(5)
p_d(8)
p_d(10)

Uh! errore 💥 Manca la funzione vale_dieci().

Errore mio: l’ho definita come valeDieci(), correggo ed ecco:

manca-2.py

def pari(n):
    print(n, "è pari")

def dispari(n):
    print(n, "è dispari")

def vale_dieci():
    print('Dieci')

def p_d(n):
    ok = False
    if n % 2 == 0:
        ok = True
        pari(n)
    elif n % 2 == 1:
        dispari(n)

    if ok and (n == 10):
        vale_dieci()

# main

p_d(5)
p_d(8)
p_d(10)

OK! 😁

Personalmente non sapevo che l’interprete Python ignora le parti di codice che non deve eseguire immediatamente. Nei casi precedenti la chiamata alla funzione vale_dieci() non era richiesta e la sua mancanza non veniva rilevata. Ho provato con interpreti di altri linguaggi e il comportamento è analogo. Credo che questo non capiti con i linguaggi compilati (ma almeno anticamente il Fortran…), sarebbe da verificare.

Una curiosità: il nome di una funzione in Python è solo una variabile come tutte le altre. Esempio:

rinomina.py

def nome_vero(msg):
    print(msg, "\n")

#main
myfun = nome_vero

myfun("ciao!")
myfun("😊 😊 😊")

Va da sé che per me –sono vecchio– questo, se non per motivi particolari, va evitato.

coreano.py

def 안녕(msg):
    print(msg, "\n")

#main
myfun = 안녕

myfun("ciao! 안녕")
myfun("😊 😊 😊")

Sì in questo caso è OK; ma chi ha scritto quella funzione? E se dico “annyong” vado bene? E com’è che vale per loro ma non per tutti (cioè non per me)?

🤩

Essere semplici

Python è uno dei miei linguaggi preferiti. Recentemente mi è capitato di aggiornare script vecchi, scritti da me e da altri, di parecchi non si conosce l’autore. Ho materiale per un paio di post, oggi comincio con uno elementare ma per me che sono niubbo di una certa importanza.

Mi è successo di trovare, più volte, delle espressioni come queste:

Il risultato è chiaro:

e se ripeto l’istruzione torno al valore precedente

Non so se è corretto ma da noi questo è conosciuto come flip-flop (lo so che è anche un’altra cosa 😋).

Ma –tranne casi particolari– secondo me non si deve usare. Scrivere invece ok = True o ok = False. Il perché è semplice: nel debug (o aggiornamento) non devo ricordarmi il valore corrente per negarlo (ovviamente deve saperlo il programmatore iniziale).

Inoltre c’è il duck typing e Python ne è dentro fino al collo. Una variabile non solo varia (OK, come fanno le variabili ma adesso sono indaffarato con la programmazione funzionale) ma varia anche il tipo. E capitano cose come queste:

Siamo passati in modo trasparente da int a bool.

In certi linguaggi si definisce false come 0 e true come 1, o, –spesso– come non 0. È il nostro caso?

No, non esattamente: c’è identità tra 0 e False e tra 1 e True ma per gli altri valori non si sa.

Non ci limitiamo agli interi

Tra le versioni 2.x e 3.x è cambiata la sintassi per la valutazione dell’ugualianza tra valori. Mentre prima si usava <> per dire diverso adesso si deve scrivere !=

Ma capitano cose impreviste

cioè in questo caso è diverso da entrambi i possibili valori booleani. In questi casi –che non si dovrebbero usare– fare sempre il cast:

Dovrebbe essere possibile anche questo

anche se potrebbero insorgere problemi di arrotondamento.

E si può trafficare anche con le stringhe (ht _s 😁):

Non so se questo post ha una qualche valenza. Ma a me è capitato 😡

🤩