CodeGym /Corso Java /Python SELF IT /Errori standard

Errori standard

Python SELF IT
Livello 20 , Lezione 1
Disponibile

7.1 Uso errato delle espressioni come valori di default per gli argomenti delle funzioni

Hai già imparato molto, vediamo di analizzare gli errori più comuni dei principianti - è meglio imparare dagli errori degli altri, vero?

Python ti permette di specificare che una funzione può avere argomenti opzionali, assegnando loro un valore di default. Questa è una caratteristica molto comoda, ma può portare a conseguenze spiacevoli se il tipo di tale valore è mutabile. Ad esempio, consideriamo la seguente definizione di funzione:


def foo(bar=[]):  # bar - questo è un argomento opzionale
# e di default è una lista vuota.
    bar.append("baz")  # questa riga può diventare un problema...
    return bar

Un errore comune in questo caso è pensare che il valore dell'argomento opzionale venga impostato al valore di default ogni volta che la funzione viene chiamata senza un valore per quell'argomento.

Nel codice sopra, ad esempio, si potrebbe supporre che richiamando di nuovo la funzione foo() (cioè senza specificare un valore per l'argomento bar), essa ritorni sempre ["baz"], poiché si presume che ogni volta che foo() viene chiamato (senza specificare l'argomento bar), bar viene impostato su [] (cioè una nuova lista vuota).

Ma vediamo cosa accade realmente:


result = foo()
print(result)  # ["baz"]
            
result = foo()
print(result)  # ["baz", "baz"]
            
result = foo()
print(result)  # ["baz", "baz", "baz"]

Perché la funzione continua ad aggiungere il valore baz alla lista esistente ogni volta che viene chiamata foo() invece di creare una nuova lista ogni volta?

La risposta sta in una comprensione più approfondita di cosa succede sotto il cofano di Python. In particolare: il valore di default per una funzione viene inizializzato solo una volta, al momento della definizione della funzione.

Pertanto, l'argomento bar viene inizializzato con il valore di default (cioè, una lista vuota) solo la prima volta che foo() viene definito, mentre le chiamate successive a foo() (senza specificare l'argomento bar) continueranno a utilizzare la stessa lista che è stata creata per l'argomento bar al momento della prima definizione della funzione.

Per riferimento, un comune "workaround" per questo errore è la seguente definizione:


def foo(bar=None):             
    if bar is None:
        bar = []                                          
    bar.append("baz")   
    return bar
    
result = foo()
print(result)  # ["baz"]
    
result = foo()
print(result)  # ["baz"]
    
result = foo()
print(result)  # ["baz"]

7.2 Uso errato delle variabili di classe

Consideriamo il seguente esempio:


class A(object):
    x = 1
       
class B(A):
    pass
       
class C(A):
    pass
       
print(A.x, B.x, C.x) 
# 1 1 1

Sembra tutto a posto.

Ora assegniamo un valore alla variabile x della classe B:


B.x = 2
print(A.x, B.x, C.x)
# 1 2 1

Tutto come previsto.

Adesso cambiamo la variabile di classe A:


A.x = 3
print(A.x, B.x, C.x)
# 3 2 3

Strano, abbiamo solo modificato A.x. Perché anche C.x è cambiato?

In Python, le variabili di classe sono gestite come dizionari e seguono ciò che spesso viene chiamato Ordine di Risoluzione dei Metodi (MRO). Quindi, nel codice sopra, dato che l'attributo x non viene trovato nella classe C, verrà trovato nelle sue classi base (solo A nell'esempio sopra, anche se Python supporta l'ereditarietà multipla).

In altre parole, C non ha una propria proprietà x, indipendente da A. Pertanto, i riferimenti a C.x sono in realtà riferimenti a A.x. Questo può causare problemi se non si gestiscono correttamente tali casi. Nel studiare Python, fai particolare attenzione agli attributi di classe e al loro utilizzo.

7.3 Uso errato dei parametri per il blocco di eccezione

Supponiamo che tu abbia il seguente pezzo di codice:


try:
    l = ["a", "b"]
    int(l[2])
except ValueError, IndexError:  # Per catturare entrambe le eccezioni, giusto?
    pass
        
Traceback (most recent call last):
    File "<stdin>", line 3, in <module>
IndexError: list index out of range

Il problema qui è che l'espressione except non accetta un elenco di eccezioni specificato in questo modo. Di conseguenza, nel codice sopra, l'eccezione IndexError non viene catturata dall'espressione except; invece, l'eccezione finisce per essere legata a un parametro di nome IndexError.

Importante! Questo codice puoi trovarlo in esempi su Internet perché è stato usato in Python 2.x.

Il modo corretto per catturare più eccezioni con un'espressione except è specificare il primo parametro come una tupla contenente tutte le eccezioni che desideri catturare. Inoltre, per una migliore leggibilità, puoi usare la parola chiave as, come segue:


try:
    l = ["a", "b"]
    int(l[2])
except (ValueError, IndexError) as e:  
    pass
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION