2.11. Condizionali¶
Un condizionale esegue un blocco di codice solo quando un certo test risulta vero. La parola chiave è if, seguita facoltativamente da una o più diramazioni elif («else if») e da un else finale.
n = 42
if n > 0:
print("positive")
elif n < 0:
print("negative")
else:
print("zero")
Il corpo di ciascuna diramazione è tutto ciò che è indentato sotto di essa (quattro spazi per convenzione). Python percorre le diramazioni in ordine, esegue la prima il cui test è vero e salta le restanti. Il blocco else viene eseguito solo se ogni test precedente è risultato falso; è sempre facoltativo.
Viene sempre eseguita una sola diramazione. I test vengono valutati dall’alto verso il basso finché uno non ha successo; i restanti vengono saltati.¶
2.11.1. Veridicità¶
Un test in un if non deve necessariamente restituire True o False – qualsiasi valore conta come vero (truthy) o falso (falsy). I valori falsy sono:
Tutto il resto è truthy. Questo ti permette di scrivere test compatti:
if name: # false on empty string
print("hello", name)
if items: # false on empty list, dict, etc.
process(items)
Tieni presente che la veridicità cambia il significato. if value: non è la stessa cosa di if value is not None: – la prima è falsa anche quando value è 0 o "". Quando intendi davvero «questo è esattamente None«, usa esplicitamente is None / is not None.
2.11.2. Espressioni ternarie¶
Un condizionale può comparire all’interno di un’espressione:
label = "even" if n % 2 == 0 else "odd"
Si legge come «label è "even" se n % 2 == 0 altrimenti "odd"«. Comodo per le righe singole; per qualsiasi cosa più lunga di una riga, un’istruzione if completa è più facile da leggere.
2.11.3. Annidamento e ritorni anticipati¶
I condizionali possono annidarsi arbitrariamente in profondità, ma ogni livello aggiuntivo di indentazione rende una funzione più difficile da leggere. L’esempio seguente controlla quattro condizioni prima di svolgere il lavoro vero e proprio e lascia la riga utile sepolta a quattro livelli di indentazione:
def process(item):
if item is not None:
if item.is_valid():
if item.size() > 0:
if item.owner == "me":
return do_the_work(item)
return None
Due schemi appiattiscono questo tipo di codice.
2.11.3.1. Ritorni anticipati per i guard¶
Gestisci prima ogni caso di «uscita», ciascuno con il proprio return, così che la logica principale rimanga al livello di indentazione esterno. Ogni guard si legge come «questo non è un caso che gestiamo; esci»:
def process(item):
if item is None:
return None
if not item.is_valid():
return None
if item.size() == 0:
return None
if item.owner != "me":
return None
return do_the_work(item)
Il «percorso principale» è ora un’unica riga in fondo alla funzione, non sepolta dentro quattro livelli. Questo stile è talvolta chiamato schema delle guard clause.
2.11.3.2. Combinare i test con and / or¶
Quando più condizioni devono valere tutte per la stessa diramazione, combinale con and anziché annidarle. Le condizioni che ciascuna attiva indipendentemente la diramazione si combinano con or:
# all must hold -- use `and`
if user.is_admin() and user.has_permission("write") and not locked:
save()
# any one of them is enough -- use `or`
if c == " " or c == "\t" or c == "\n":
whitespace_count += 1
Entrambe le forme adottano la valutazione a corto circuito, quindi un controllo costoso sul lato destro viene eseguito solo quando i controlli più economici sulla sinistra non hanno già risolto la questione.