Comandamenti
Il Comitato Supremo per la Dottrina del Coding ha emanato importanti comandamenti che seguirai scrupolosamente.
Se accetti le loro sagge parole, diventerai un vero Jedi Python.
ATTENZIONE: se non segui i Comandamenti, finirai nel Debugging Hell !
I COMANDAMENTO
Scriverai codice Python
Chi non scrive codice Python, non impara Python
II COMANDAMENTO
Quando inserisci una variabile in un ciclo for
,
questa variabile deve essere nuova
Se hai definito la variabile prima, non la reintrodurrai in un for
, perchè ciò portebbe confusione nelle menti di chi legge.
Perciò evita questi peccati:
[1]:
i = 7
for i in range(3): # peccato, perdi la variabile i
print(i)
print(i) # stampa 2 e non 7 !!
0
1
2
2
[2]:
def f(i):
for i in range(3): # altro peccato, perdi il parametro i
print(i)
print(i) # stampa 2, e non il 7 che gli abbiamo passato !
f(7)
0
1
2
2
[3]:
for i in range(2):
for i in range(5): # inferno da debuggare, perdi l'i del ciclo for esterno
print(i)
print(i) # stampa 4 !!
0
1
2
3
4
4
0
1
2
3
4
4
III COMANDAMENTO
Non riassegnerai mai parametri di funzione
Non farai mai nessuna di queste assegnazioni, pena la perdita del parametro passato quando viene chiamata la funzione:
[4]:
def peccato(intero):
intero = 666 # peccato, hai perso il 5 passato dall'esterno !
print(intero) # stampa 666
x = 5
peccato(x)
666
Lo stesso discorso si applica per tutti gli altri tipi:
[5]:
def male(stringa):
stringa = "666"
[6]:
def disgrazia(lista):
lista = [666]
[7]:
def delirio(dizionario):
dizionario = {"evil":666}
Per il solo caso di parametri compositi come liste o dizionari, puoi scrivere come sotto SE E SOLO SE le specifiche della funzione ti richiedono di MODIFICARE gli elementi interni del parametro (come per esempio ordinare una lista o cambiare il campo di un dizionario)
[8]:
# MODIFICA lista in qualche modo
def consentito(lista):
lista[2] = 9 # OK, lo richiede il testo della funzione
fuori = [8,5,7]
consentito(fuori)
print(fuori)
[8, 5, 9]
[9]:
# MODIFICA dizionario in qualche modo
def daccordo(dizionario):
dizionario["mio campo"] = 5 # OK, lo richiede il testo
[10]:
# MODIFICA istanza in qualche modo
def va_bene(istanza_di_classe):
istanza_di_classe.mio_campo = 7 # OK, lo richiede il testo
Se invece il testo di una funzione ti chiede di RITORNARE un NUOVO oggetto, non cadrai nella tentazione di modificare l’input:
[11]:
# RITORNA una NUOVA lista ordinata
def dolore(lista):
lista.sort() # MALE, stai modificando la lista di input invece di crearne una nuova!
return lista
[12]:
# RITORNA una NUOVA lista
def crisi(lista):
lista[0] = 5 # MALE, come sopra
return lista
[13]:
# RITORNA un NUOVO dizionario
def tormento(dizionario):
dizionario['a'] = 6 # MALE, stai modificando il dizionario di input
# invece di crearne uno nuovo!
return dizionario
[14]:
# RITORNA una NUOVA istanza di classe
def disperazione(istanza):
istanza.mio_campo = 6 # MALE, stai modificando l'oggetto di input
# invece di crearne uno nuovo!
return istanza
IV COMANDAMENTO
Non riassegnerai mai valori
a chiamate a funzioni o metodi
mia_funzione() = 666 # SBAGLIATO
mia_funzione() = 'evil' # SBAGLIATO
mia_funzione() = [666] # SBAGLIATO
x = 5 # OK
y = my_fun() # OK
z = [] # OK
z[0] = 7 # OK
d = dict() # OK
d["a"] = 6 # OK
Chiamate a funzione come mia_funzione()
ritornano risultati di calcoli e li mettono in una scatola che è creata solo per lo scopo della chiamata e Python non ci consentirà di riusarla come una variabile.
Quando vedi nome()
alla parte sinistra, non può essere seguito da un segno di uguaglianza =
(ma può essere seguito da due segni di uguaglianza ==
se stai eseguendo una comparazione).
V COMANDAMENTO
Non ridifinerai mai funzioni di sistema
Python ha diverse funzioni di sistema predefinite. Per esempio list
è un tipo Python: come tale, puoi usarlo per esempio come funzione per convertire un qualche tipo a lista:
[15]:
list("ciao")
[15]:
['c', 'i', 'a', 'o']
Quando consenti alle forze del male di prendere il sopravvento, potresti essere tentato di usare tipi e funzioni di sistema (per es. list
) come una variabile per i tuoi miserabili propositi personali:
list = ['la', 'mia', 'lista', 'raccapricciante']
Python ti permette di farlo, ma noi no, poichè le conseguenze sono disastrose.
Per esempio, se adesso usi list
per il proposito per cui è stata creata, cioè conversione a lista, non funzionerà più:
list("ciao")
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-4-c63add832213> in <module>()
----> 1 list("ciao")
TypeError: 'list' object is not callable
In particolare, raccomandiamo di non ridefinire queste preziose funzioni:
bool
,int
,float
,tuple
,str
,list
,set
,dict
max
,min
,sum
next
,iter
id
,dir
,vars
,help
VI COMANDAMENTO
Userai il comando return
solo se vedi scritto RITORNA nella descrizione di funzione!
Se non c’è un RITORNA nella descrizione di funzione, si intende che la funzione ritorni None
. In questo caso non devi nemmeno scrivere return None
, perchè Python lo farà implicitamente per te.
VII COMANDAMENTO
Scriverai anche su carta!
Se fissare il monitor non funziona, aiutati e disegna su carta una rappresentazione dello stato del programma. Tabelle, nodi, frecce, tutto può aiutare nel trovare una soluzione al problema.
VIII COMANDAMENTO
Non riassegnerai mai self
!
Non scriverai mai empietà come questa:
[16]:
class MiaClasse:
def mio_metodo(self):
self = {'mio_campo':666}
Dato che self
è una specie di dizionario, potresti essere tentato di scrivere come sopra, ma al mondo esterno questo non porterà alcun effetto.
Per esempio, supponiamo che qualcuno da fuori faccia una chiamata come questa:
[17]:
mc = MiaClasse()
mc.mio_metodo()
Dopo la chiamata mc
non punterà a {'mio_campo':666}
[18]:
mc
[18]:
<__main__.MiaClasse at 0x7fe98843d8d0>
e non avrà mio_campo
:
mc.mio_campo
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-26-5c4e6630908d> in <module>()
----> 1 mc.mio_campo
AttributeError: 'MiaClasse' object has no attribute 'mio_campo'
Per lo stesso ragionamento, non devi riassegnare self
a liste o altro:
[19]:
class MiaClasse:
def mio_metodo(self):
self = ['evil']
self = 666
IX COMANDAMENTO
Testerai il codice!
Il codice non testato per definizione non funziona. Per idee su come testare, guarda Gestione degli errori e testing
X COMANDAMENTO
Non aggiungerai o toglierai mai elementi da una sequenza che iteri con un for
!
Abbandonarti a simil tentazioni produrrebbe comportamenti del tutto imprevedibili (conosci forse l’espressione tirare il tappeto da sotto i piedi? )
Non aggiungere, poichè rischi di camminare su un tapis roulant che mai si spegne:
lista = ['a','b','c','d','e']
for el in lista:
lista.append(el) # STAI INTASANDO LA MEMORIA DEL COMPUTER
Non togliere, poichè rischi di corrompere l’ordine naturale delle cose:
[20]:
lista = ['a','b','c','d','e']
for el in lista:
lista.remove(el) # PESSIMA IDEA
Guarda bene il codice. Credi che abbiamo rimosso tutto, eh?
[21]:
lista
[21]:
['b', 'd']
O_o'
Non provar a capacitarti di cotal sortilegio - nessuno capirlo può, poichè esso è legato all’implementazione interna di Python.
La mia versione di Python dà questo risultato assurdo, la vostra potrebbe darne un’altro. Il discorso vale anche per iterazione su insiemi e dizionari. Siete avvertiti.
Se proprio devi rimuovere elementi dalla sequenza su cui stai iterando, usa un ciclo while o effettua prima una copia della sequenza originale.