Python Language
Python Anti-Patterns
Ricerca…
Overzealous tranne clausola
Le eccezioni sono potenti, ma una singola clausola eccessivamente zelante può portare via tutto in un'unica riga.
try: res = get_result() res = res[0] log('got result: %r' % res) except: if not res: res = '' print('got exception')
Questo esempio dimostra 3 sintomi dell'antiPattern:
- L'
except
con nessun tipo di eccezione (riga 5) otterrà eccezioni anche salutari, inclusoKeyboardInterrupt
. Ciò impedirà al programma di uscire in alcuni casi. - Il blocco except non genera un errore, il che significa che non saremo in grado di stabilire se l'eccezione proviene da
get_result
o perchéres
era una lista vuota. - Peggio ancora, se eravamo preoccupati che il risultato fosse vuoto, abbiamo causato qualcosa di molto peggio. Se
get_result
fallisce,res
rimarrà completamente disinserito, e il riferimento ares
nel blocco except, solleveràNameError
, mascherando completamente l'errore originale.
Pensa sempre al tipo di eccezione che stai cercando di gestire. Dai una lettura alla pagina delle eccezioni e fatti un'idea delle eccezioni di base esistenti.
Ecco una versione fissa dell'esempio sopra:
import traceback try: res = get_result() except Exception: log_exception(traceback.format_exc()) raise try: res = res[0] except IndexError: res = '' log('got result: %r' % res)
Rileviamo eccezioni più specifiche, controrilanciare ove necessario. Qualche altra riga, ma infinitamente più corretta.
Guardando prima di saltare con la funzione intensiva del processore
Un programma può facilmente perdere tempo chiamando più volte una funzione intensiva del processore.
Ad esempio, prendi una funzione che assomiglia a questa: restituisce un intero se il value
input può produrne uno, altrimenti None
:
def intensive_f(value): # int -> Optional[int]
# complex, and time-consuming code
if process_has_failed:
return None
return integer_output
E potrebbe essere usato nel modo seguente:
x = 5
if intensive_f(x) is not None:
print(intensive_f(x) / 2)
else:
print(x, "could not be processed")
print(x)
Mentre questo funzionerà, ha il problema di chiamare intensive_f
, che raddoppia il tempo di esecuzione del codice. Una soluzione migliore sarebbe ottenere in anticipo il valore di ritorno della funzione.
x = 5
result = intensive_f(x)
if result is not None:
print(result / 2)
else:
print(x, "could not be processed")
Tuttavia, un modo più chiaro e possibilmente più pitoni è usare le eccezioni, ad esempio:
x = 5
try:
print(intensive_f(x) / 2)
except TypeError: # The exception raised if None + 1 is attempted
print(x, "could not be processed")
Qui non è necessaria alcuna variabile temporanea. Spesso può essere preferibile utilizzare una dichiarazione assert
e catturare l' AssertionError
.
Chiavi del dizionario
Un esempio comune di dove questo può essere trovato è l'accesso alle chiavi del dizionario. Ad esempio confronta:
bird_speeds = get_very_long_dictionary()
if "european swallow" in bird_speeds:
speed = bird_speeds["european swallow"]
else:
speed = input("What is the air-speed velocity of an unladen swallow?")
print(speed)
con:
bird_speeds = get_very_long_dictionary()
try:
speed = bird_speeds["european swallow"]
except KeyError:
speed = input("What is the air-speed velocity of an unladen swallow?")
print(speed)
Il primo esempio deve esaminare il dizionario due volte e, poiché questo è un dizionario lungo, potrebbe richiedere molto tempo per farlo ogni volta. Il secondo richiede solo una ricerca attraverso il dizionario, risparmiando così molto tempo del processore.
Un'alternativa a questo è usare dict.get(key, default)
, tuttavia molte circostanze potrebbero richiedere operazioni più complesse da eseguire nel caso in cui la chiave non sia presente