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.

Un diagramma di flusso che mostra if/elif/else: due test decisionali a rombo con i loro corpi, che ricadono in un corpo else finale quando entrambi i test sono falsi.

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:

  • False e None

  • Il numero zero: 0, 0.0

  • Sequenze e collezioni vuote: "", [], (), {}, b""

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.