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.