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.
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:
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.