Python: comprensione e utilizzo di if-else

L’istruzione if-else in Python è una ramificazione condizionale e quindi una struttura di controllo di base. L’uso di una ramificazione consente di seguire percorsi di codice diversi a seconda di una condizione riscontrata in fase di esecuzione. Vi spieghiamo come funziona e come si usa if-else in Python.

Certificati SSL di IONOS

Proteggi il tuo dominio e ottieni la fiducia dei clienti con un sito crittografato tramite SSL!

Facile attivazione
Sicurezza
Supporto 24/7

Che cos’è un’istruzione if-else?

L’istruzione if-else è una ramificazione condizionale. Si può pensare che sia come un interruttore: a seconda dell’elemento di commutazione, viene seguito solo uno dei due percorsi possibili. Python è un linguaggio interpretato; l’interprete legge le righe del codice sorgente dall’alto verso il basso. Nel caso più semplice, si ottiene un flusso di programma strettamente lineare: tutte le righe vengono lette, interpretate ed eseguite una dopo l’altra.

Seguendo questo schema semplice, tuttavia, non è possibile realizzare programmi complessi. È solo attraverso l’uso di strutture di controllo che si ottiene la variabilità del codice eseguito, richiesta nell’uso pratico. Una ramificazione consente l’esecuzione condizionale di alcune parti di codice. In particolare, i cicli for in Python e i cicli while in Python sono utilizzati come ulteriori strutture di controllo, che consentono l’esecuzione ripetuta di parti di codice.

Consiglio

Imparate a scrivere nel codice Python con il nostro tutorial su Python!

Come funziona if-else in Python?

In generale, l’istruzione if-else in Python si comporta in modo simile a quello conosciuto in altri linguaggi. Tuttavia, esistono alcune peculiarità specifiche di Python. Vediamo in dettaglio come funziona l’istruzione if-else in Python e come si comporta rispetto ad altri linguaggi.

Sintassi generale dell’istruzione if-else in Python

La sintassi generale dell’istruzione if-else in Python può essere espressa direttamente nel codice Python. Definiamo una condizione e nel corpo dell’istruzione if, ad esempio, specifichiamo una chiamata di funzione come percorso di codice da seguire quando la condizione diventa vera. Inoltre, nel corpo dell’istruzione else, definiamo un percorso di codice che verrà seguito in caso contrario, ovvero nel caso in cui la condizione non diventi mai vera:

if condition:
    if_body()
else:
    else_body()

La condizione definita può essere vera (True) o falsa (False). Vi chiariamo meglio il modello inserendo il Literal True o False direttamente come condizione. Il risultato è un flusso di programma statico in cui è garantito che solo uno dei due percorsi verrà seguito:

if False:
    # questo codice non verrà mai eseguito
    if_body()
else:
    else_body()
if True:
    if_body()
else:
    # questo codice non verrà mai eseguito
    else_body()

Naturalmente, questo schema non è pratico, ma serve solo per chiarire meglio il concetto. Invece di un valore statico vero/falso, viene utilizzata un’espressione come condizione. L’espressione viene valutata in fase di esecuzione del programma. In questo caso si parla di “valutazione” dell’espressione. Quando l’espressione viene valutata, si ottiene un valore di verità. A seconda che il valore di verità sia True o False, il programma si dirama in una direzione o nell’altra.

È importante capire che la parte else è facoltativa. Il codice nel corpo dell’istruzione else viene eseguito solo se la condizione non è valida. Tuttavia, ciò non è assolutamente necessario. Spesso è sufficiente una dichiarazione if indipendente:

if condition:
    if_body()

Una piccola nota sulla nomenclatura: abbiamo usato i termini “istruzione” ed “espressione” senza spiegarli. È importante capire cosa si intende con questi termini, visto che sono utilizzati in tutti i linguaggi di programmazione. Di seguito vi riportiamo il significato dei termini:

Termine Equivalente in inglese Spiegazione
Istruzione/Dichiarazione Statement Azione che viene eseguita; di solito influenza il corso del programma
Espressione Expression Termine che restituisce un valore quando viene valutato

Interrogare più condizioni esclusive con l’istruzione elif in Python

Oltre alla ben nota istruzione if-else, in Python esiste anche l’istruzione elif. Un’istruzione if è facoltativamente seguita da diverse istruzioni elif, seguite da un blocco else opzionale. Un’istruzione elif viene eseguita solo se nessuna delle condizioni precedenti è diventata vera. Questo garantisce che venga seguito un solo percorso di codice definito:

if condition:
    if_body()
elif other_condition:
    elif_body()
elif another_condition:
    another_elif_body()
else:
    else_body()

Di seguito trovate le regole per costruire una ramificazione concatenata if-elif-else:

Istruzione di ramificazione Quantità nella combinazione
if Proprio una
elif Zero o più
else Zero o una

Come si usa if-else in Python?

L’istruzione if-else è una funzionalità di base in Python come in altri linguaggi. Naturalmente, gli usi sono molteplici. Vi mostriamo degli esempi comuni, comprese le migliori pratiche e gli anti-pattern.

Uso corretto della condizione if in Python

Vediamo innanzitutto come funziona la condizione di un’istruzione di ramificazione. La condizione viene interpretata come un’espressione booleana che valuta uno dei valori di verità True o False. Non è quindi necessario verificare esplicitamente l’uguaglianza con un letterale booleano:

if expression == True:
    ...

Anche se la corrispondenza esplicita con True non è un vero errore, il codice appare poco professionale. I programmatori esperti scrivono invece:

if expression:
    ...

Analizziamo l’anti-pattern con un esempio. Supponiamo che una funzione is_odd() restituisca True per un numero se questo è dispari. Altrimenti, la funzione is_odd restituisce False. Utilizziamo la funzione all’interno di un’istruzione if e produciamo un testo corrispondente:

if is_odd(number) == True:
   print("The number is odd.")

Immaginiamo cosa succede quando si passa dalla ramificazione con un numero dispari. In primo luogo, l’espressione “is_odd(number) == True” è valutata come “True == True”. Quest’ultimo, a sua volta, viene valutato come “True”; il corpo dell’istruzione if viene eseguito. È più sensato usare il valore booleano restituito dalla funzione is_odd direttamente come condizione:

if is_odd(number):
   print("The number is odd.")

Esecuzione di codice opzionale con dichiarazione if in Python

Immaginiamo la seguente situazione: abbiamo un blocco di codice con determinate funzioni. Le righe vengono eseguite una dopo l’altra. Tuttavia, una parte del codice non deve essere eseguita sempre, ma solo quando una condizione è vera. Per implementare questo schema, abbiamo bisogno solo di un’istruzione if. Qui viene mostrato l’esempio di un processo classico di registrazione dell’utente:

def register_user(user_data, do_newsletter_signup = False):
    # create user account
    user_account = create_account(user_data)
    # sign up for newsletter — only if requested by user
    if do_newsletter_signup:
        signup_newsletter(user_account)
    # send confirmation mail
    send_confirmation_mail(user_account)

Distinguere due casi distinti con l’istruzione if-else in Python

Spesso i programmi devono distinguere tra due casi che si escludono a vicenda. Se è logicamente ovvio che non possono esistere altri casi, ha senso utilizzare un’istruzione if-else. Ad esempio, verifichiamo se una persona ha raggiunto una certa età e forniamo un risultato adeguato a seconda dei casi:

def is_of_age(person, age_limit = 18):
    if person.age >= age_limit:
        print("You're old enough")
    else:
        print("Sorry, wait some more")

Distinguere più casi esclusivi con le istruzioni elif in Python

Se si vogliono distinguere più di due casi mutuamente esclusivi, entrano in gioco le dichiarazioni elif concatenate. Un else finale viene utilizzato per rilevare le variazioni sconosciute. Assegniamo i nomi dei Paesi ai codici corrispondenti:

def get_country_from_code(country_code):
    if country_code == 'DE':
        country = "Deutschland"
    elif country_code == 'ES':
        country = "España"
    elif country_code == 'FR':
        country = "France"
    elif country_code == 'GB':
        country = "Great Britain"
    elif country_code == 'IT':
        country = "Italia"
    else:
        country = None
    return country

Se la catena elif è all’interno di una funzione, a volte è meglio utilizzare più dichiarazioni if indipendenti. In questo modo ci risparmiamo l’assegnazione nel corpo dell’istruzione elif. Con l’istruzione return, usciamo dalla funzione se una delle condizioni diventa vera. Al posto dell’else finale, viene utilizzata per ultima un’istruzione return, che viene raggiunta solo se nessuna delle condizioni è vera:

def get_country_from_code(country_code):
    if country_code == 'DE':
        return "Deutschland"
    if country_code == 'ES':
        return "España"
    if country_code == 'FR':
        return "France"
    if country_code == 'GB':
        return "Great Britain"
    if country_code == 'IT':
        return "Italia"
    return None

Le dichiarazioni elif concatenate sono un modello ben noto nei linguaggi più datati. In Python, spesso è più diretto usare un “Dictionary Lookup”, ossia cercare su un dizionario. Definiamo direttamente l’assegnazione dei codici ai nomi dei Paesi ed estraiamo il nome in base al codice. Al posto dell’istruzione finale else, utilizziamo il metodo incorporato get, che accetta un valore predefinito come secondo parametro:

def get_country_from_code(country_code):
    countries = {
        'DE': "Deutschland",
        'ES': "España",
        'FR': "France",
        'GB': "Great Britain",
        'IT': "Italia",
    }
    country = countries.get(country_code, None)
    return country

Utilizzo dell’istruzione if in Python per verificare se un oggetto contiene dati

Python è un linguaggio dinamico e non tipizzato. Invece delle variabili, i tipi sono legati ai valori. A seconda dell’uso, possono verificarsi conversioni implicite tra tipi. Nel contesto delle espressioni booleane, si parla anche di “truthy” e “falsy” come estensione dei valori di verità True e False.

Ciò significa che la condizione di un’istruzione if in Python non deve essere valutata esplicitamente come True o False. Piuttosto, i valori di altri tipi possono essere usati come condizioni. Questi vengono interpretati come valori booleani in base a determinate regole.

 

Citazione

“Any object can be tested for truth value, for use in an if or while condition or as operand of the Boolean operations […]

[...] An object is considered true unless its class defines either a __bool__() method that returns False or a __len__() method that returns zero [...]” – Fonte: docs.python.org/3/library/stdtypes.html

Traduzione: “È possibile verificare il valore di verità di qualsiasi oggetto per utilizzarlo in un’istruzione if o while o in una delle operazioni booleane [...]

[...] Un oggetto è valutato vero a meno che non definisca un metodo __bool__() che restituisce False o un metodo __len__() che restituisce zero [...]” (tradotto da IONOS).

Utilizziamo questo schema per verificare se un oggetto contiene dati. Poiché il testo seguente è vuoto, viene restituito un messaggio corrispondente:

text = ''
if not text:
    print("No text given")

I seguenti oggetti vengono valutati come False in un contesto booleano e sono pertanto definiti “falsy”:

Oggetto Spiegazione
False, None Costanti che sono False in base alla definizione
0, 0.0, Decimal(0), Fraction(0, 1), ecc. Numero che rappresenta lo zero
‘‘, (), [], {}, set(), range(0), ecc. Sequenza o insieme vuoti

Un altro esempio con una lista vuota:

books_in_library = []
if not books_in_library:
    print("Library is empty")

Tutti gli altri oggetti vengono valutati come True

number = 42
if number:
    print("Number exists")

Implementare un interruttore binario con if-else in Python

Un’istruzione if-else viene utilizzata anche per andare avanti e indietro tra due stati esclusivi. Il principio è simile a quello di un interruttore della luce e viene chiamato “toggle” in inglese. Definiamo una funzione che alterna lo stato di una luce:

def toggle_light(light):
    if light == 'on':
        return 'off'
    else:
        return 'on'

Forse avrete già notato che è ancora più facile. Se lo stato non è rappresentato come una stringa ma come un valore booleano, è possibile rinunciare completamente all’istruzione if. Si utilizza invece l’operatore logico NOT per invertire il valore booleano:

def toggle(boolean):
    return not boolean

Uso di Early Return per risolvere le dichiarazioni if annidate in Python

In pratica, è comune eseguire un certo codice quando diverse condizioni diventano vere allo stesso tempo. Questo porta rapidamente a dichiarazioni if annidate per i programmatori inesperti. Tuttavia, questo non è un buon comportamento da seguire. Infatti, le ramificazioni annidate in profondità sono difficili da tenere sotto controllo e da gestire.

Facciamo un esempio: scriviamo una funzione che restituisca la dichiarazione se una persona può votare. Per prima cosa controlliamo se la persona ha un documento d’identità. Poi verifichiamo se la persona ha già raggiunto l’età per votare. Una prima implementazione della funzione contiene dichiarazioni if annidate:

def person_may_vote(person, voting_age = 18):
    if person.has_id():
        if person.get_age() >= voting_age:
            return True

Un problema immediato di questa implementazione è che più condizioni vengono verificate, più il codice più importante viene indentato. Per risolvere le dichiarazioni if annidate, si utilizza solitamente l’istruzione “Early Return”. Quindi, all’inizio della funzione controlliamo se le singole condizioni sono soddisfatte. Se una condizione non è soddisfatta, interrompiamo e usciamo dalla funzione utilizzando l’istruzione return.

Riformuliamo la nostra funzione per utilizzare le istruzioni Early Return. Spesso ciò richiede l’inversione delle condizioni precedentemente definite. In questo caso è utile la comprensione degli operatori booleani in Python. Se non si applica nessuna delle condizioni negative, viene eseguito il codice effettivamente interessante:

def person_may_vote(person, voting_age = 18):
    if not person.has_id():
        return False
    if person.age < voting_age:
        return False
    # if we made it here, the person may vote
    return True

Uso degli operatori logici per semplificare e sostituire le istruzioni if in Python

Come abbiamo già visto, spesso è necessario controllare che si verifichino più condizioni. Nella maggior parte dei casi, è subottimale utilizzare dichiarazioni if annidate per verificare più condizioni. Esaminiamo il codice di esempio che determina se una persona può votare:

if has_id(person):
    if is_adult(person):
        print("You may vote")

Le condizioni concatenate possono essere rappresentate nel modo migliore utilizzando gli operatori logici. Poiché vogliamo verificare se entrambe le condizioni sono valide allo stesso tempo, utilizziamo l’operatore logico AND. In questo modo abbiamo bisogno di una sola dichiarazione if:

if has_id(person) and is_adult(person):
    print("You may vote")

Qui di seguito trovate gli operatori logici di base in Python:

Operatore logico Significato Sintassi Python Altri linguaggi    
E L’espressione è vera se tutti gli operandi sono veri; restituisce l’ultimo operando valutato. and &&    
O L’espressione è vera se almeno uno degli operandi è vero; restituisce l’ultimo operando valutato. or      
NON Inverte il valore dell’espressione. not !    

Vediamo un esempio: immaginiamo che un/una utente possa specificare una valuta per un calcolo finanziario. Per facilitarne l’uso, la scelta dovrebbe essere facoltativa. Se l’utente non seleziona una valuta, il valore predefinito è EUR. Il codice seguente traccia questo principio:

# user made no currency choice
currency = None
...
# further down in the program flow
if not currency:
    # set default if value missing
    currency = 'EUR'

Utilizzando l’operatore logico OR, l’istruzione if può essere risolta. Riscriviamo il codice; quando viene eseguito, la variabile currency contiene il valore “EUR”:

# user made no currency choice
currency = None
...
# further down in the program flow
# set default if value missing
currency = currency or 'EUR'

Cosa succede esattamente qui? Nell’ultima riga, viene assegnato un nuovo valore alla variabile “currency”. A tal fine, viene valutata prima l’espressione “currency or 'EUR'” sul lato destro del segno di uguale. L’operatore logico OR valuta innanzitutto l’espressione di sinistra, in questo caso “currency”. Poiché nell’esempio contiene “None” ed è quindi “falsy”, l’espressione corretta “EUR” viene valutata e utilizzata come valore di ritorno per l’assegnazione.

L’operatore condizionale if-else in Python

Oltre alla ramificazione condizionale, c’è un altro uso delle parole chiave if-else in Python. È l’operatore condizionale, noto anche come operatore “ternario”, molto usato per distinguere tra due possibili valori nelle assegnazioni.

Vediamo prima un esempio che utilizza l’istruzione if-else in Python. Il codice seguente imposta Celsius o Fahrenheit come unità di misura della temperatura, a seconda del sistema di misura selezionato:

if system == 'metric':
    unit = 'C'
else:
    unit = 'F'

Utilizzando l’operatore condizionale, il codice può essere semplificato in una singola assegnazione:

unit = 'C' if system == 'metric' else 'F'

Come svela il nome, l’operatore ternario accetta tre operandi: i due valori possibili e un’espressione come condizione.

Operatore Spiegazione Esempio
Unario L’operatore accetta un operando. not boolean_operand
Binario L’operatore accetta due operandi. left_operand + right_operand
Ternario L’operatore accetta tre operandi. some_value if condition else other_value

Sostituzione di if-else in Python con l’istruzione match-case

Con il rilascio della versione 3.10 di Python, è stata introdotta l’istruzione match-case. All’inizio ricorda l’istruzione switch-case di altri linguaggi. In questo caso, si usa switch-case per spezzare i costrutti if-elif-else di grandi dimensioni.

Noto per essere soggetto a errori, switch-case non è mai esistito in Python. Piuttosto, l’istruzione match-case in Python è una funzionalità per lo “Structural Pattern Matching” (in italiano: “corrispondenza strutturale dei modelli”) basata su linguaggi funzionali come Haskell. La sua utilità va ben oltre quella dello switch-case.

Illustriamo il principio di match-case con un esempio: immaginiamo di voler elaborare dati di persone in formati diversi. Una persona può essere rappresentata come un singolo nome o come un dizionario con nome ed eventualmente età o come una tupla di nome e cognome. Inoltre, vogliamo gestire il nome esatto “Jack” in modo particolare:

# the name 'Jack'
person1 = 'Jack'
# just a name
person2 = 'John'
# name and age in a dict
person3 = {'name': 'Jim', 'age': 42}
# name in a dict, but no age
person4 = {'name': 'Walter', 'email': 'walter.white@example.com'}
# tuple of first and last name
person5 = ('Walther', 'White')

Vediamo innanzitutto una funzione che saluta una persona in uno dei formati. Utilizziamo una catena if-elif-else e la funzione isinstance per distinguere i diversi formati. Inoltre, vengono utilizzate condizioni concatenate con l’operatore AND e un’istruzione if-else annidata. Il codice finale non sembra particolarmente chiaro:

def greet_person(person):
    if isinstance(person, str):
        if person == 'Jack':
            print('Jack himself has arrived')
        else:
            print(f"Hi there, {person}")
    elif isinstance(person, dict) and 'name' in person and 'age' in person:
        print(f"It's, {person['name']}. Born {person['age']} years ago")
    elif isinstance(person, dict) and 'name' in person:
        print(f"It's {person['name']}")
    elif isinstance(person, tuple) and len(person) == 2:
        first, last = person
        print(f"Hello, {first} {last}")
    else:
        print('Not sure what kind of person this is')

Il codice può essere costruito in modo più elegante con l’istruzione match-case. Descriviamo direttamente la struttura dei singoli formati; i singoli valori possono essere estratti come variabili. Il codice è più chiaro, meno complesso e più facile da leggere:

def match_person(person):
    match person:
        case 'Jack':
            print('Jack himself has arrived')
        case str() as name:
            print(f"Hi there, {name}")
        case {'name': name, 'age': age}:
            print(f"It's, {name}. Born {age} years ago")
        case {'name': name}:
            print(f"It's {name}")
        case (first, last):
            print(f"Hello, {first} {last}")
        case _:
            print('Not sure what kind of person this is')