2.11. Conditionals

Een conditional voert een codeblok alleen uit wanneer een test als waar wordt geëvalueerd. Het sleutelwoord is if, optioneel gevolgd door een of meer elif-takken (“else if”) en een afsluitende else.

n = 42

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

De body van elke tak is alles wat eronder ingesprongen staat (vier spaties is de conventie). Python loopt de takken in volgorde af, voert de eerste uit waarvan de test waar is, en slaat de rest over. Het else-blok wordt alleen uitgevoerd als elke voorafgaande test onwaar was; het is altijd optioneel.

Een stroomdiagram dat if/elif/else toont: twee ruitvormige beslissingstests met hun bodies, die uitkomen op een afsluitende else-body wanneer beide tests onwaar zijn.

Er wordt altijd maar één tak uitgevoerd. Tests worden van boven naar beneden geëvalueerd totdat er een slaagt; de rest wordt overgeslagen.

2.11.1. Waarheidswaarde

Een test in een if hoeft niet True of False te retourneren – elke waarde telt als truthy of falsy. De falsy waarden zijn:

  • False en None

  • Het getal nul: 0, 0.0

  • Lege reeksen en verzamelingen: "", [], (), {}, b""

Al het overige is truthy. Hiermee kun je compacte tests schrijven:

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

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

Houd er rekening mee dat waarheidswaarde de betekenis verandert. if value: is niet hetzelfde als if value is not None: – de eerste is ook onwaar wanneer value gelijk is aan 0 of "". Wanneer je echt bedoelt “is dit precies None“, gebruik dan expliciet is None / is not None.

2.11.2. Ternaire expressies

Een conditional kan binnen een expressie voorkomen:

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

Lees als “label is "even" als n % 2 == 0 anders "odd".” Handig voor oneregelaars; voor alles wat meer dan één regel is, is een volledige if-instructie makkelijker te lezen.

2.11.3. Nesten en vroege returns

Conditionals kunnen willekeurig diep genest worden, maar elke extra laag inspringing maakt een functie moeilijker te lezen. Het onderstaande voorbeeld controleert vier voorwaarden voordat het echte werk wordt gedaan en laat de nuttige regel vier inspringingen diep verborgen liggen:

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

Twee patronen maken dit soort code platter.

2.11.3.1. Vroege returns als bewakers

Behandel elk “afbreek”-geval eerst, elk met zijn eigen return, zodat de hoofdlogica op de buitenste inspringing blijft. Elke bewaker leest als “dit is geen geval dat we behandelen; vertrek”:

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)

Het “hoofdpad” is nu één regel onderaan de functie, niet verborgen binnen vier lagen. Deze stijl wordt soms het guard clause-patroon genoemd.

2.11.3.2. Tests combineren met and / or

Wanneer meerdere voorwaarden allemaal moeten gelden voor dezelfde tak, combineer ze dan met and in plaats van te nesten. Voorwaarden die elk afzonderlijk de tak activeren, combineer je met 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

Beide vormen evalueren met kortsluiting, dus een dure controle aan de rechterkant wordt alleen uitgevoerd wanneer de goedkopere controles aan de linkerkant de vraag nog niet hebben beslist.