To be or not to be
In Python gibt es diese schöne Möglichkeit:
if not a:
do something
Das kommt in verschiedenen Geschmacksrichtungen wie:
a = a or self.default
oder
a = a if a else self.default
Man schreibt also nicht was a (eine Variable) nicht ist (nicht True, nicht 0 usw.) bzw. was sie ist, sondern einfach das sie nicht ist bzw. ist.
Ich habe das schon so benutzt und auch schon häufiger in anderem Code gesehen.
Es sieht elleganter aus und ist schneller zu lesen, als z.B.:
if a is not None:
do something
Man muss dabei aber aufpassen, denn es ist ein Unterschied ob man a is not None oder not a schreibt.
Der Check ob eine Variable "ist" oder nicht "ist" meint nicht das die Variable None sein muss um "nicht" zu sein.
Praktisch wird hier impliziet auf "False" geprüft, dabei stehen folgende Werte impliziet für False:
-
False
-
0
-
None
-
"" (leerer String)
-
[] (leere Liste)
-
{} (leeres Dictionary)
-
() (leeres Tupple
In all den Fällen (und mehr) würde:
if not a:
do something
also True ergeben und "do something" ausgeführt werden.
Es gibt Fälle wo das erwünscht ist und Fälle wo es nicht erwünscht ist, wie hier:
1
2
3
4
5
6
7
8
9
10
11
12
from time import sleep
class Something:
def __init__(self):
self.default_delay = 5
def print_wait(self, message, delay=None):
delay = delay or self.default_delay
print(f"{message}. I will sleep for {delay} seconds now."
sleep(delay)
a = Something()
a.print_wait("Hallo", 0)
-
Zeile 6 setzt delay auf None wenn delay auf keinen Wert gesetzt wird
-
Zeile 7 prüft ob delay gesetzt ist und wenn "nicht" bekommt delay den Wert von self.default_delay
-
in der Deklaration einer Methode (Zeile 6) kann nicht auf self zugegriffen werden, deshalb der Umweg
-
Im Aufruf in Zeile 12 wird als delay-Parameter 0 übergeben.
Man könnte also erwarten das die Ausgabe:
Hallo. I will sleep for 0 seconds now.
wäre.
Das Ergebnis ist aber:
Hallo. I will sleep for 5 seconds now.
Das liegt daran das Zeile 7 tut:
delay = delay or self.default_delay
Es wird geprüft ob delay "Wahr" ist oder nicht und da "0" nicht "Wahr" ist wird der Wert aus self.default_delay gesetzt.
Richtig wäre an dieser Stelle:
delay = delay if delay is not None else self.default_delay
zu nutzen. Es prüft auf None (und nur darauf) und setzt im Falle das es None ist self.default_delay.
Fazit
Implizite checks ala:
c = a or b
if not a:
c = a if not b else b
sollte man vermeiden, da sie einen sehr breiten Bereich von Werten abdecken, was ggf. zu unerwarteten Ergebnissen führen kann.
Es sei denn man möchte genau diesen breiten Bereich an Werten abdecken.
Man sollte in Unittests explizit Paramater auf Wertebereiche testen die auch implizit als False interpretiert werden könnten.
Also zum Beispiel 0, wenn der Parameter ein int entgegen nehmen kann oder "", im Falle das es ein String ist oder leere Listen oder leere andere Objekte.
Dadurch findet man Probleme wie im Beispiel oben schneller.