2.11. Conditionnelles

Une conditionnelle n’exécute un bloc de code que lorsqu’un certain test est évalué à vrai. Le mot-clé est if, éventuellement suivi d’une ou plusieurs branches elif (« else if ») et d’un else final.

n = 42

if n > 0:
    print("positive")
elif n < 0:
    print("negative")
else:
    print("zero")

Le corps de chaque branche est tout ce qui est indenté en dessous (quatre espaces par convention). Python parcourt les branches dans l’ordre, exécute la première dont le test est vrai, et ignore les autres. Le bloc else ne s’exécute que si tous les tests précédents étaient faux ; il est toujours optionnel.

Un organigramme illustrant if/elif/else : deux losanges de décision avec leurs corps, débouchant sur un corps else final lorsque les deux tests sont faux.

Une seule branche s’exécute jamais. Les tests sont évalués de haut en bas jusqu’à ce que l’un réussisse ; les autres sont ignorés.

2.11.1. Valeur de vérité

Un test dans un if n’a pas à renvoyer True ou False – toute valeur compte comme vraie (truthy) ou fausse (falsy). Les valeurs fausses sont :

  • False et None

  • Le nombre zéro : 0, 0.0

  • Les séquences et collections vides : "", [], (), {}, b""

Tout le reste est vrai. Cela vous permet d’écrire des tests compacts :

if name:                     # false on empty string
    print("hello", name)

if items:                    # false on empty list, dict, etc.
    process(items)

Sachez que la valeur de vérité change le sens. if value: n’est pas identique à if value is not None: – la première est également fausse lorsque value vaut 0 ou "". Lorsque vous voulez réellement dire « est-ce exactement None », utilisez explicitement is None / is not None.

2.11.2. Expressions ternaires

Une conditionnelle peut apparaître à l’intérieur d’une expression :

label = "even" if n % 2 == 0 else "odd"

À lire comme « label vaut "even" si n % 2 == 0 sinon "odd" ». Pratique pour les expressions en une ligne ; pour tout ce qui dépasse une ligne, une instruction if complète est plus facile à lire.

2.11.3. Imbrication et retours anticipés

Les conditionnelles peuvent s’imbriquer à une profondeur arbitraire, mais chaque niveau d’indentation supplémentaire rend une fonction plus difficile à lire. L’exemple ci-dessous vérifie quatre conditions avant d’effectuer le vrai travail et laisse la ligne utile enfouie quatre indentations plus loin :

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

Deux schémas permettent d’aplatir ce genre de code.

2.11.3.1. Retours anticipés pour les gardes

Traitez d’abord chaque cas d”« abandon », chacun avec son propre return, afin que la logique principale reste au niveau d’indentation extérieur. Chaque garde se lit comme « ce n’est pas un cas que nous traitons ; on sort » :

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)

Le « chemin principal » est désormais une seule ligne au bas de la fonction, et non enfoui dans quatre niveaux. Ce style est parfois appelé schéma des clauses de garde (guard clause).

2.11.3.2. Combiner des tests avec and / or

Lorsque plusieurs conditions doivent toutes être vraies pour une même branche, combinez-les avec and plutôt que de les imbriquer. Les conditions qui déclenchent chacune indépendamment la branche se combinent avec 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

Les deux formes évaluent en court-circuit, de sorte qu’une vérification coûteuse à droite ne s’exécute que lorsque les vérifications moins coûteuses à gauche n’ont pas déjà tranché la question.