Archivi Categorie: Python

Un quiz & considerazioni personali relative

Cosa non si trova ontehtoobz! (auto-cit.) 😯 Ieri questo: I was just shown this. Guess what this does in Python? powers = [lambda x: x**i for i in range(10)] powers[3](2).

Sukant è conosciuto, in diversi socials, rockz. E, confesso, ho provato a risolvere il suo quiz, nel senso di correggere la funzione, senza riuscirci. Poi ho letto i commenti, già David A Roberts 💥 doveva mettermi sulla retta via:

* quiz $ py3
>>> def f(i): return lambda x: x**i
...
>>> powers = [f(i) for i in range(10)]
>>> powers[3](2)
8

ma ci sono altri come me che non sono così pronti: How is it Alex Jones got banned, and yet Sukant still has a twitter account after posting this?? dice Warner e Leftmost Grief Node: Haha the Pythonistas responding to this in my org are like, “Well that code is not Pythonic so…”.

das portal ci spiega il perché, Dan P ci dice che Scala…

Ma la risposta migliore è quella di jeeger che si potrebbe tradurre con *RTFM!*, in questo caso The Hitchhiker’s Guide to Python, in particolare –chiaro come il sole– Late Binding Closures.

Non copio è là.
Da leggere attentamente, anzi studiare che poi interrogo, nèh! 🧐

Ci ho pensato su. Anzi ci ho dormito su. E ho maturato una mia opinione che potrò cambiare se e solo se fatti o circostanze me lo consiglieranno.

In questo caso –e tanti altri casi simili– il problema non esiste:

>>> pow(2, 3)
8
>>> pow(2, 0.5)
1.4142135623730951
>>> pow(-2, 0.5)
(8.659560562354934e-17+1.4142135623730951j)

Non ce l’ho con lambda e le funzioni e quant’altro ma con il loro (ab)uso. E usare solo quello che si conosce davvero. Detto in altri termini non ho ancora digerito il fatto che non ho trovato il bug 👿

Comunque…

Siate semplici (cit.). Anzi, qui. È talmente autoevidente che non si sa di chi è; è Patrimonio dell’Umanità (se non lo è dovrebbe esserlo).

Formattare le stringhe in Python

Python lo uso da sempre (cioè no, ho solo dimenticato quando l’ho scoperto (o meglio il numero della versione che mi sembra di ricordar è troppo basso per essere credibile) e capita che ogni tanto scopro di non essere aggiornato, sapete l’evilussione 🧐 Per fortuna c’è Twitter che mi suggerisce, via prof Corno ecco: Python 3’s f-Strings: An Improved String Formatting Syntax (Guide).

La documentazione ufficiale corrente di Python (3.6 o mi sto perdendo qualcosa anche qui? sì. c’è la 3.7 nuova di pakka, non ancora installata) è aggiornata e dice tutto, anche se in modo un po’ troppo sintetico per quelli come me.

A formatted string literal or f-string is a string literal that is prefixed with ‘f‘ or ‘F‘.

Conviene quindi seguire la guida di RealPython, anzi RealPython è da followare su Twitter, fatto.

Anzi, la guida è talmente OK che non continuo a scrivere, c’è tutto là. A me resterebbe di aggiornare gazillioni di script che usano non immaginate cosa (l’evilussione) ma poi mi viene da pensare che (cit.)

anche perché capita troppo spesso che parto con l’idea di aggiornare e mi scopro a riscrivere ⭕

Perché il ciclo for di Python è speciale

Il titolo avrebbe dovuto essere “perché range(start, end) non comprende end?” ma si sarebbe perso il riferimento a for.

Forse lo sanno tutti, forse qualcuno l’ha scoperto da poco, altri ancora forse no. E perché?  Sto dicendo del ciclo for in Python.

Nella riscrittura di in Python di un programmino Basic c’è stata un po’ di confusione sugli indici di arrays e contatori di cicli.

Utilizzo MY-BASIC di Wang Renxin per visualizzare il ciclo for come lo intende il Basic, in tutte le sue varianti e incarnazioni, in particolare nel mio caso si tratta di VB, Visual Basic.

run e bye sono estensioni di MY-BASIC, il comando mb invece è un alias mio ma l’esempio –minimo– illustra come il Basic gestisce il ciclo, in particolare il ciclo comprende il limite finale, 5 in questo caso. Lo stesso comportamento lo troviamo nel linguaggio da cui il Basic deriva, il Fortran (dove il ciclo si chiama do). L’istruzione può prevedere un passo per l’indice, così:

OK? Ma questa è un’eccezione, dal C in poi il ciclo for ha una sintassi diversa, per esempio con nodejs (un sapore di JavaScript) posso scrivere:

Ovviamente si può scrivere <= al posto di < comprendendo così il limite superiore. Con C e C++ la sintassi è la stessa, migliora solo l’istruzione di scrittura, printf() e/o cout <<.

Il for per Python è diverso, funziona con le liste:

Potrei usare una lista di numeri:

Per creare una lista di numeri, come quelle che si trovano di solito nei cicli for esiste la funzione range().

The range type represents an immutable sequence of numbers and is commonly used for looping a specific number of times in for loops.

Come si vede il limite finale non è mai compreso. Per cui il ciclo iniziale (quello dei numeri da 1 a 5) sarà:

Visto 6 e non 5.

C’è un potivo per tutto ciò, il BDFL (emerito da due giorni) Guido la sa lunga:

in questo modo la lunghezza (len()) della lista è uguale al limite superiore del range(). Altre cosa da ricordare (già usata implicitamente ma è bene ricordarla per quelli come me che vengono dal Fortran) è che gli indici partono da 0, zero.

Questo è spiegato più in dettaglio qui.

E per chi vuole saperne di più passo la parola a EDW, qui: Why numbering should start at zero.

Sostituire i TABs e i caratteri non-ASCII

A volte il testo preso dal Web o da altri posti ancora non è come lo vorremmo. Capita che contenga caratteri strani, per esempio TABs, caratteri Unicode (a volte sono OK, a volte no, per esempio nel codice apici e virgolette devono essere quelli classici anche se meno belli), λ (OK ma non nella REPL di Racket (sì, questo solo per me)). Ecco il terminale è uno di quelli che certe cose proprio non le accetta.

Vero che in (quasi) qualsiasi editor si può fare ma se del caso…

Comincio con TAB, serve come base poi generalizzabile (file stab):

#!/bin/bash

if [ -z $1 ]; then
  echo 'uso:' $0 '[sost-char] file'
  exit 2
elif [ -z $2 ]; then
  SOST=' '
  PROC=$1
else
  SOST=$1
  PROC=$2
fi

sed "s/\t/$SOST/g" $PROC

Lanciato senza opzioni esce con un messaggio:

È richiesto il nome del file preceduto opzionalmente con il carattere (o i caratteri) che sostituiranno il TAB.

OK? C’è (ancora, pare che adesso Windows 10 sia sulla buona strada) chi preferisce Python. Si può fare; anzi lo script risulta simile (stab.py):

#!/usr/bin/python3

import sys, re

if len(sys.argv) == 1:
  print ('uso:', sys.argv[0], '[sost-char] file')
  exit(2)
elif len(sys.argv) == 2:
  sost = ' '
  proc = sys.argv[1]
else:
  sost = sys.argv[1]
  proc = sys.argv[2]

with open(proc, 'r') as f:
  txt = f.readlines()

for st in txt:
  print(re.sub('\t', sost, st), end='')

È possibile evitare l’uso delle espressioni regolari (modulo re che ho preferito per uniformità con lo script seguente): basta sostiture l’ultimo ciclo for con

for st in txt:
  print(st.replace('\t', sost), end='')

Generalizzando arrivo ad andare alla ricerca di tutti i caratteri fuori dalla sequenza ASCII. Ho copiato in un file un paio di tweets (dimenticando di salvare il nome degli autori) contenenti citazioni quotate. Nel primo si usano le virgolette che si trovano nella stampa tedesca, nel secondo no, roba corrente ontehtoobz. Ho aggiunto un paio di emoji, ormai hanno invaso il Web. Poi passo a evidenziali:

Questo è lo script fuori.py:

#!/usr/bin/python3

import sys, re

if len(sys.argv) == 1:
  print ('uso:', sys.argv[0], '[sost-char] file')
  exit(2)
elif len(sys.argv) == 2:
  sost = '_'
  proc = sys.argv[1]
else:
  sost = sys.argv[1]
  proc = sys.argv[2]

with open(proc, 'r') as f:
  txt = f.readlines()

for st in txt:
  print(re.sub(r'[^\x00-\x7f]', sost, st), end='')

Procedo come per il TAB ma con sed ci sono problemi, dovuti alla tabella caratteri caricata se abbiamo capito bene. Ancora da indagare (e risolvere), materia per un prossimo post.

OK, 😁⭕

Globale o locale?

Jake 💥 rockz e oggi ci racconta di un caso che neanche Dirk Gentlyquesto.

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 earlier print(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)

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…

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 😡

🤩