HOWTO zu den Booleschen Wahrheitswerten in Python

(C) 2016-2024 T.Birnthaler/H.Gottschalk <howtos(at)ostc.de>
              OSTC Open Source Training and Consulting GmbH
              www.ostc.de

Dieses Dokument beschreibt die Boolesche Logik der Werte in Python,
insbesondere die Booleschen Werte True und False sowie den Wert None.

Inhaltsverzeichnis

1) Boolesche Werte
1b) Boolescher Kontext
1c) Darstellung per print/str/repr
1d) Formatierte Ausgabe
1e) Zusammenhang mit Zahlen-Datentypen int/float/complex
2) Logische Operatoren and, or, not und ihr Vorrang
2a) Vorrang
2b) Inhaltliches Ergebnis
2c) Tatsächliches Ergebnis
2d) Short Cut/Circuit Evaluation
2e) Rechenausdrücke mit logischen Werten (Expression)
2f) Bitweise Operatoren
2g) Logische Operator xor
3) Vergleiche
3a) True == 1/1.0 und False == 0/0.0
3b) Test auf leeren/gefüllten Container
4) Undefinierter Wert None
4a) 3-wertige Logik
4b) 3-wertige SQL-Logik

1) Boolesche Werte   (Toc)

Doku --> docs.python.org/3/library/stdtypes.html#truth
     --> docs.python.org/3/library/stdtypes.html#boolean-type-bool
     --> docs.python.org/3/library/stdtypes.html#boolean-values
     --> docs.python.org/3/library/stdtypes.html#truth-value-testing
     --> docs.python.org/3/library/stdtypes.html#boolean-operations-and-or-not
     --> docs.python.org/3/library/stdtypes.html#comparisons

Python kennt den BOOLESCHEN Datentyp "bool" mit den Werten "True" und "False",
aber interpretiert auch JEDEN anderen Wert JEDES Datentyps als LOGISCHEN Wert.
Dabei ist EXAKT DEFINIERT, welche Werte als logisch "falsch" und welche als
logisch "wahr" interpretiert werden. Logisch "falsch" sind GENAU die folgenden
Werte (alle anderen WERTE sind logisch "wahr"):

  * Konstante "None" (steht für Wert "undefiniert/ungültig/unbekannt")
  * Konstante "False" (falsch)
  * Wert "0" numerischer Datentypen (int, float, complex, Decimal, Fraction)
  * Leere Sequenzen (str, list, tuple)
  * Leere Collections (dict, set)
  * Leere Generatoren (range)

Vollständige Liste:

  +------------------+-------------------------------------------------------+
  | Wert             | Beschreibung                                          |
  +------------------+-------------------------------------------------------+
  | None             | Undefinierter/ungültiger/unbekannter Wert             |
  | False            | Logisch falscher Wert                                 |
  +------------------+-------------------------------------------------------+
  | 0 -0             | Ganzzahl Null                                         |
  | 0.0  -0.0  0e0   | Fließkommazahl Null                                   |
  | 0l  0L           | Ganzzahl Null (Datentpy "long", NUR in Python2!)      | PY2
  | 0j  0J  -0j  -0J | Imaginäre Zahl Null (Imaginärteil komplexe Zahl)      |
  | Decimal(0)       | Dezimalzahl 0 (interessant für Finanzbuchhaltung)     |
  | Fraction(0,1)    | Bruch 0/1                                             |
  +------------------+-------------------------------------------------------+
  | ""               | Leere Zeichenkette (auch ''  r""  r''  """""" ...)    |
  | ()               | Leerer Tupel                                          |
  | []               | Leere Liste                                           |
  | {}               | Leeres Dictionary                                     |
  | set()            | Leere Menge                                           |
  | range(0)         | Leerer Wertebereich                                   |
  +------------------+-------------------------------------------------------+
  | bytes()          | Leere Byte-Folge                                      |
  | bytearray()      | Leere Byte-Folge                                      |
  | frozenset()      | Leere Menge (im-mutable)                              |
  | ...              | ...                                                   |
  +------------------+-------------------------------------------------------+

ALLE ANDEREN Werte werden als logisch "wahr" interpretiert, z.B. folgende
(Auswahl):

  +----------------+-------------------------------------------+
  | Wert           | Bedeutung                                 |
  +----------------+-------------------------------------------+
  | True           | Logisch wahrer Wert                       |
  | 1              | Zahl 1                                    |
  | -1             | Negative Zahl -1                          |
  | 0.1            | Fließkommazahl 0.1                        |
  | Decimal("0.1") | Dezimalzahl 0.1                           |
  | Fraction(1,2)  | Bruch 1/2                                 |
  +----------------+-------------------------------------------+
  | " "            | Zeichenkette 1 Leerzeichen                |
  | "     "        | Zeichenkette 5 Leerzeichen                |
  | "\n"           | Zeichenkette 1 Newline                    |
  | "\r"           | Zeichenkette 1 Carriage Return            |
  | "\t"           | Zeichenkette 1 Tabulator                  |
  | "None"         | Zeichenkette Text "None"                  |
  | "False"        | Zeichenkette Text "False"                 |
  | "0"            | Zeichenkette 1 Zeichen "0"                |
  | "00"           | Zeichenkette 2 Zeichen "00"               |
  | "+0"           | Zeichenkette 2 Zeichen "+0"               |
  | "-0"           | Zeichenkette 2 Zeichen "-0"               |
  | "0.0"          | Zeichenkette 3 Zeichen "0.0"              |
  | "abc"          | Beliebige Zeichenkette ab 1 Zeichen Länge |
  +----------------+-------------------------------------------+
  | (0,)           | Tupel mit mind. 1 Wert                    |
  | [0]            | Liste mit mind. 1 Wert                    |
  | {0:0}          | Dictionary mit mind. 1 Wert               |
  | {0}            | Set mit mind. 1 Wert                      |
  | range(1)       | Wertebereich mit mind. 1 Wert             |
  +----------------+-------------------------------------------+

HINWEIS: In JEDER Programmiersprache gibt es eine EXAKTE DEFINITION, welche
Werte als logisch "Falsch" und welche als logisch "Wahr" interpretiert werden.
Üblich ist auch, dass JEDER Wert IMPLIZIT als Boolescher Wert interpretiert
wird (z.B. als Bedingung in einer if-Verzweigung oder while-Schleife).

EXPLIZIT erfolgt die Konvertierung in einen Booleschen Wert durch:

  bool(WERT)     # Konvertierung (Python)
  not not WERT   # 2-malige Negation (Python, SQL, ...)
  !!WERT         # 2-malige Negation (C, C++, Java, ..., NICHT in Python!)

1b) Boolescher Kontext in if/while   (Toc)

Die Anweisungen "if" und "while" erzwingen einen "Booleschen Kontext", in dem
der Wert des steuernden Ausdrucks EXPR (Expression/Bedingung) als Boolescher
Wert interpretiert wird:

  if EXPR: ...       # Entspricht if bool(EXPR): ...
  while EXPR: ...    # Entspricht while bool(EXPR): ...

1c) Darstellung per print/str/repr   (Toc)

Die Booleschen Werte True und False sowie der "undefinierte" Wert None werden
bei Ausgabe per print(...) bzw. bei Konvertierung per str(...) oder repr(...)
als Text "True", "False" und "None" dargestellt:

  print(None)    # --> None
  print(True)    # --> True
  print(False)   # --> False
  str(None)      # --> 'None'
  str(True)      # --> 'True'
  str(False)     # --> 'False'
  repr(None)     # --> 'None'
  repr(True)     # --> 'True'
  repr(False)    # --> 'False'

1d) Formatierte Ausgabe   (Toc)

Die Booleschen Werte True und False werden im Rahmen der FORMATIERTEN AUSGABE
folgendermaßen als Ganzzahl (d=decimal), Fließkommazahl (f=float, 6 Nkst.) und
String (s=str) dargestellt:

  print("%d" % True)            # --> 1
  print("%f" % True)            # --> 1.000000
  print("%s" % True)            # --> True
  print("{:d}".format(True))    # --> 1
  print("{:f}".format(True))    # --> 1.000000
  print("{:}".format(True))     # --> True
  print("{}".format(True))      # --> True
  print(F"{True:d}")            # --> 1
  print(F"{True:f}")            # --> 1.000000
  print(F"{True:}")             # --> True
  print(F"{True}")              # --> True

  print("%d" % False)           # --> 0
  print("%f" % False)           # --> 0.000000
  print("%s" % False)           # --> False
  print("{:d}".format(False))   # --> 0
  print("{:f}".format(False))   # --> 0.000000
  print("{:}".format(False))    # --> False
  print("{}".format(False))     # --> False
  print(F"{False:d}")           # --> 0
  print(F"{False:f}")           # --> 0.000000
  print(F"{False:}")            # --> False
  print(F"{False}")             # --> False

Mit Format "s" (str) ergeben "True" und "False" einen Fehler (seltsamerweise):

  print("{:s}".format(True))    # --> ValueError: Unknown format code 's' for object of type 'bool'
  print("{:s}".format(False))   # --> ValueError: Unknown format code 's' for object of type 'bool'
  print(F"{True:s}")            # --> ValueError: Unknown format code 's' for object of type 'bool'
  print(F"{False:s}")           # --> ValueError: Unknown format code 's' for object of type 'bool'

Der Wert "None" kann formatiert nur ohne Formatangabe dargestellt werden:

  print("%s" % None)            # --> None
  print("{}".format(None))      # --> None
  print(F"{None:}")             # --> None
  print(F"{None}")              # --> None

Mit Format "d" (int) und "f" (float) und "s" (str) ergibt  "None" einen Fehler:

  print("%d" % None)            # --> TypeError: %d format: a real number is required, not NoneType
  print("%f" % None)            # --> TypeError: must be real number, not NoneType
  print("{:d}".format(None))    # --> TypeError: unsupported format string passed to NoneType.__format__
  print("{:f}".format(None))    # --> TypeError: unsupported format string passed to NoneType.__format__
  print("{:s}".format(None))    # --> TypeError: unsupported format string passed to NoneType.__format__
  print(F"{None:d}")            # --> TypeError: unsupported format string passed to NoneType.__format__
  print(F"{None:f}")            # --> TypeError: unsupported format string passed to NoneType.__format__
  print(F"{None:s}")            # --> TypeError: unsupported format string passed to NoneType.__format__

1e) Zusammenhang mit Zahlen-Datentypen int/float/complex   (Toc)

Der Datentyp "bool" ist eine SPEZIALISIERUNG des Datentyps "int", d.h. die
Booleschen Werte "True/False" werden in Rechenausdrücken als Ganzzahl 1/0
interpretiert:

  True + 100                        # --> 101
  False + 100                       # --> 100
  True * 100                        # --> 100
  False * 100                       # --> 0
  True + False + 1 + 1.0 + (1+1j)   # --> (4+1j)
  True * False * 1 * 1.0 * (1+1j)   # --> 0j

In Listen/Tupeln werden "True/False" als Index 1/0 interpretiert:

  lst = ["hallo", "welt", "hier", "bin", "ich"]
  print(lst[False])   # --> hallo
  print(lst[True])    # --> welt

Ebenso werden "True/False" in Dictionaries/Sets mit den Keys 1/0 (int), 1.0/0.0
(float) und 1.0+0.0j/0.0+0.0j (complex) IDENTIFIZIERT (d.h. jeder der folgenden
Keys kann nur 1x in einem Dictionary oder Set vorkommen).

  True    1   1.0   1.0+0.0j   1+0j
  False   0   0.0   0.0+0.0j   0+0j

Vor PY3.7 wurde bei gleichzeitiger Verwendung der obigen Keys ein ZUFÄLLIGER
Key + sein Wert ausgewählt, seit PY3.7 wird der ERSTE im Dictionary vorkommende
Key mit dem LETZTEN diesem Key zugeordneten Wert ausgewählt:

  dct = {True:  "bool", 1: "int", 1.0: "float", 1+0j: "complex"}
  print(dct)              # --> {True: 'complex'}
  dct = {}                #
  dct[True] = "bool"      #
  dct[1]    = "int"       #
  dct[1.0]  = "float"     #
  dct[1+0j] = "complex"   #
  print(dct)              # --> {True: 'complex'}
  dct = {False: "bool", 0: "int", 0.0: "float", 0j: "complex"}
  print(dct)              # --> {False: 'complex'}
  dct = {}                #
  dct[False] = "bool"     #
  dct[0]     = "int"      #
  dct[0.0]   = "float"    #
  dct[0j]    = "complex"  #
  print(dct)              # --> {False: 'complex'}

2) Logische Operatoren and, or, not   (Toc)

Die Booleschen Operatoren AND, OR und NOT zur logischen Verknüpfung von Werten
werden durch die Schlüsselworte and, or und not dargestellt.

Die aus den Programmiersprachen C/C++/... bekannten Syntaxformen && || ! für
die Booleschen Operatoren gibt es in Python nicht!

Den Booleschen Operator XOR gibt es nicht als Schlüsselwort, er lässt sich aber
durch den Operator ^ darstellen (Bitweises-XOR) falls zwei Boolesche Werte
verknüpft werden.

2a) Vorrang   (Toc)

Bei KOMBINATION der logischen Operatoren "and", "or" und "not" in einem
Ausdruck gilt folgender VORRANG (wie in allen anderen Programmiersprachen
auch):

  +-----+-------------+
  | not | höchster    |
  | and | mittlerer   |
  | or  | niedrigster |
  +-----+-------------+

2b) Inhaltliches Ergebnis   (Toc)

Die logischen Operatoren "and", "or" und "not" liefern angewendet auf
Wahrheitswerte (oder beliebige andere Werte, die dann gemäß obiger Tabelle als
Wahrheitswerte interpretiert werden) folgende Resultate:

  +------------+-----------------------------------------------------------+
  | Operator   | Bedeutung                                                 |
  +------------+-----------------------------------------------------------+
  | A and B    | UND: Resultat "True" wenn beide Operanden A und B True    |
  |            |      Resultat "False" sonst                               |
  | A or B     | ODER: Resultat "False" wenn beide Operanden A und B False |
  |            |       Resultat "True" sonst                               |
  | not A      | NICHT: Resultat "True" wenn Operand A False               |
  |            |        Resultat "False" wenn Operand A True               |
  +------------+-----------------------------------------------------------+

2c) Tatsächliches Ergebnis   (Toc)

Der logische Operator "not" liefert IMMER True oder False zurück:

  not True    # --> False
  not False   # --> True
  not "abc"   # --> False
  not ""      # --> True
  not 0       # --> True
  not 1       # --> False
  not []      # --> True

Die logischen Operatoren "and" und "or" liefern IMMER den LETZTEN für das
Ergebnis ausgewerteten Wert (interpretierbar als Wahrheitswert) zurück:

  5 or "abc"     # --> 5       = True
  1 or ["abc"]   # --> 1       = True
  0 or "abc"     # --> 'abc'   = True
  0 or ""        # --> ''      = False (leerer String)
  1 and 234      # --> 234     = True
  1 and ["abc"]  # --> ['abc'] = True
  9 and None     # --> None    = False
  0 and []       # --> 0       = False

2d) Short Cut/Circuit Evaluation   (Toc)

Die logische Operatoren and und or werden VON LINKS NACH RECHTS ausgewertet
(unter Berücksichtigung ihres Vorrangs). Ergibt sich dabei ein
Zwischenergebnis, bei dem auch das ENDERGEBNIS des gesamten logischen Ausdrucks
feststeht, wird die Auswertung an dieser Stelle abgebrochen und der Rest des
logischen Ausdrucks nicht mehr ausgewertet ("Short Cut/Circuit Evaluation",
verkürzte Auswertung). Dieses Verhalten ist KEINE Besonderheit von Python,
sondern ist in vielen Programmiersprachen (z.B. C, C++, Java, JavaScript, Perl,
PHP, Ruby, Awk) genauso üblich.

* Folge von UND-Verknüpfungen (and) bricht mit Gesamtergebnis False ab,
  sobald 1x False als Zwischenergebnis vorkommt.

* Folge von ODER-Verknüpfungen (or) bricht mit Gesamtergebnis True ab,
  sobald 1x True als Zwischenergebnis vorkommt.

Beispiel:

  0 and "KEINE Ausgabe"   # --> 0         = False
  1 and "Ausgabe"         # --> 'Ausgabe' = True
  1 or "KEINE Ausgabe"    # --> 1         = True
  0 or "Ausgabe"          # --> 'Ausgabe' = True
  5 or "abc" or 7.5       # --> 5         = True
  0 or "abc" or 7.5       # --> 'abc'     = True
  0 or "" or 7.5          # --> 7.5       = True
  1 and "" and 3.14       # --> ''        = False (leerer String)
  1 and 234 and 74        # --> 74        = True
  9 and None and 8        # --> None      = False
  0 or [] or {}           # --> {}        = False (leeres dict)

2e) Rechenausdrücke mit logischen Werten (Expression)   (Toc)

In Rechenausdrücken (Expression) zählt True als Zahl 1 und False als Zahl 0.
D.h. Berechnungen mit Booleschen Werten sind erlaubt:

  123 - True             # --> 122
  False + 123            # --> 123
  True * True            # --> 1
  True * False           # --> 0
  5 * True - 3 * False   # --> 5
  if monat == 2:
      max_tag = monat_laenge(monat) + schaltjahr(jahr)   # schaltjahr --> True/False
  else:
      max_tag = monat_laenge(monat)

2f) Bitweise Operatoren   (Toc)

Die Operatoren & (Bit-and), | (Bit-or), ^ (Bit-xor), ~ (Bit-invert,
Zweier-Komplement), << (Bit-shift-left) und >> (Bit-shift-right) für die
BITWEISEN Operationen lieferen eine bitweise Verknüpfung von 2 Zahlen (analog
C/C++/...).

  0b1010 & 0b1100   # Bit-and:         10 & 12 -->   8 =  0b1000
  0b1010 | 0b1100   # Bit-or:          10 | 12 -->  14 =  0b1110
  0b1010 ^ 0b1100   # Bit-xor:         10 ^ 12 -->   6 =  0b0110
  ~ 0b1010          # Bit-invert:      ~ 10    --> -11 = -0b1011 = -10 - 1
  0b1010 << 1       # Bit-shift-left:  10 << 1 -->  20 =  0b10100
  0b1010 >> 1       # Bit-shift-right: 10 >> 1 -->   5 =  0b0101

2g) Logischer Operator xor   (Toc)

Der Bitweise XOR-Operator ^ liefert normalerweise eine ganze Zahl zurück. Beim
Verknüpfen von zwei Booleschen Werten liefert er allerdings wieder einen
Booleschen Wert zurück, kann also als Ersatz für (den fehlenden) logischen
Operator "xor" verwendet werden:

  True ^ True    # --> False
  True ^ False   # --> True
  False ^ True   # --> True
  False ^ False  # --> False

HINWEIS: Die Operatoren & und | verhalten sich ebenfalls so, dass sie bei
Verknüpfung zweier Booleschen Werten wieder einen Booleschen Wert zurückliefern
und somit als Ersatz für "and" und "or" verwendet werden können.

  True & True     # --> True
  False & True    # --> False
  True & False    # --> False
  False & False   # --> False

  True | True     # --> True
  False | True    # --> True
  True | False    # --> True
  False | False   # --> False

HINWEIS: Der Operator ~ (Bitweise-Invertieren) lässt sich nicht als Ersatz für
"not" verwenden!

  ~ True    # --> -2
  ~ False   # --> -1

Die Vorrangtabelle ist dann insgesamt allerdings etwas unübersichtlich:

  +-----+-------------+
  |  &  | and-Ersatz  |
  |  ^  | XOR-Ersatz  |
  |  |  | or-Ersatz   |
  +-----+-------------+
  | not | höchster    |
  | and | mittlerer   |
  | or  | niedrigster |
  +-----+-------------+

Um den "nächsten Programmierer" nicht zu verwirren, sollten bei LOGISCHEN
VERKNÜPFUNGEN die Bit-Operatoren NICHT als Ersatz für die logischen Operatoren
verwendet werden.

3) Vergleiche   (Toc)

Folgende Vergleiche prüfen, ob eine Variable den Wert True bzw. False hat:

  if VAR:                # VAR True?                                (GUT)
  if not VAR:            # VAR False?                               (GUT)
  if VAR == True:        # --> False außer VAR hat Wert True/1/1.0  (SCHLECHT)
  if VAR == False:       # --> False außer VAR hat Wert False/0/0.0 (SCHLECHT)
  if VAR != True:        # --> True  außer VAR hat Wert True        (SCHLECHT)
  if VAR != False:       # --> True  außer VAR hat Wert False       (SCHLECHT)
  if VAR is True:        # --> False außer VAR hat Wert True        (SCHLECHT)
  if VAR is False:       # --> False außer VAR hat Wert False       (SCHLECHT)
  if VAR is not True:    # --> True  außer VAR hat Wert True        (SCHLECHT)
  if VAR is not False:   # --> True  außer VAR hat Wert False       (SCHLECHT)

ACHTUNG: Nur die beiden ersten Tests sind allgemein gültig, die anderen Tests
prüfen nur EINEN Wert/EINIGE Werte, aber nicht alle Werte gemäß Tabelle --> 1)
Boolesche Werte.

3a) True == 1/1.0 und False == 0/0.0   (Toc)

Aus historischen Gründen (der Datentyp "bool" wurde erst später als Subtyp
von "int" hinzugefügt) liefern folgende Vergleiche alle den Wert True
(obwohl die Datentypen der verglichenen Werte unterschiedlich sind):

  True  == 1     # --> True
  True  == 1.0   # --> True
  True  == 1+0j  # --> True
  1     == 1.0   # --> True
  1     == 1+0j  # --> True
  1.0   == 1+0j  # --> True
  False == 0     # --> True
  False == 0.0   # --> True
  False == 0j    # --> True
  0     == 0.0   # --> True
  0     == 0j    # --> True
  0.0   == 0j    # --> True

3b) Test auf leeren/gefüllten Container   (Toc)

Da JEDES Objekt im Booleschen Kontext einen Wahrheitswert ergibt, kann ein
Test auf leeren/gefüllten CONTAINER (str, tuple, list, dict, set, ...)
folgendermaßen durchgeführt werden:

  if CONT:            # CONTAINER enthält mind. 1 Element  --> True/False (GUT)
  if not CONT:        # CONTAINER leer                     --> True/False (GUT)

  if len(CONT) != 0:  # CONTAINER enthält mind. 1 Element  --> True/False (SCHLECHT)
  if len(CONT) > 0:   # CONTAINER enthält mind. 1 Element  --> True/False (SCHLECHT)
  if len(CONT) >= 1:  # CONTAINER enthält mind. 1 Element  --> True/False (SCHLECHT)
  if len(CONT) == 0:  # CONTAINER leer                     --> True/False (SCHLECHT)

  if TUPEL != ():     # Tupel enthält mind. 1 Element      --> True/False (SCHLECHT)
  if TUPEL == ():     # Tupel leer                         --> True/False (SCHLECHT)
  if LISTE != []:     # Liste enthält mind. 1 Element      --> True/False (SCHLECHT)
  if LISTE == []:     # Liste leer                         --> True/False (SCHLECHT)
  if DICT != {}:      # Dictionary enthält mind. 1 Eintrag --> True/False (SCHLECHT)
  if DICT == {}:      # Dictionary leer                    --> True/False (SCHLECHT)
  if STR != "":       # String enthält mind. 1 Zeichen     --> True/False (SCHLECHT)
  if STR == "":       # String leer                        --> True/False (SCHLECHT)

HINWEIS: Die beiden ersten Varianten sind vom Standpunkt der Allgemeinheit,
der Performance und des Speicherverbrauchs aus die Geschicktesten!

4) Undefinierter Wert None   (Toc)

Ein undefinierter (unbekannter/ungültiger) Wert kann in Python durch "None"
dargestellt werden (hat den Datentyp "NoneType" bzw. ab PY3.7 type(None)).
Variablen mit diesem Wert sind UNDEFINIERT (analog SQL-Wert "NULL"), ALLE
anderen Werte sind DEFINIERT.

Der Datentyp "NoneType" und sein einziger Wert "None" können eigentlich
miteinander IDENTIFIZIERT werden (SINGLETON). Ab PY3.7 gibt es daher den
Bezeichner "NoneType" für den Datentyp nicht mehr direkt, sondern nur indirekt
per type(None):

  NoneType = type(None)   # ab PY3.7

Folgende Tests prüfen, ob der Wert einer Variablen "var" DEFINIERT bzw.
UNDEFINIERT ist:

  if var is None: ...                     # var UNDEFINIERT? (EMPFOHLEN!)
  if var == None: ...                     # var UNDEFINIERT? (NICHT empfohlen)
  if type(var) == NoneType: ...           # var UNDEFINIERT? (NICHT empfohlen)
  if type(var) == type(None): ...         # var UNDEFINIERT? (NICHT empfohlen, ab PY3.7)
  if isinstance(var, NoneType): ...       # var UNDEFINIERT? (NICHT empfohlen)
  if isinstance(var, type(None): ...      # var UNDEFINIERT? (NICHT empfohlen, ab PY3.7)

  if var is not None: ...                 # var DEFINIERT? (EMPFOHLEN!)
  if not var is None: ...                 # var DEFINIERT? (NICHT empfohlen)
  if var != None: ...                     # var DEFINIERT? (NICHT empfohlen)
  if not var == None: ...                 # var DEFINIERT? (NICHT empfohlen)
  if type(var) != NoneType: ...           # var DEFINIERT? (NICHT empfohlen)
  if type(var) != type(None): ...         # var DEFINIERT? (NICHT empfohlen, ab PY3.7)
  if not isinstance(var, NoneType): ...   # var DEFINIERT? (NICHT empfohlen)
  if not isinstance(var, type(None): ...  # var DEFINIERT? (NICHT empfohlen, ab PY3.7)

Numerische Berechnungen mit "None" generieren IMMER einen "TypeError":

  None + 3    # --> TypeError!
  None - 3    # --> TypeError!
  None * 3    # --> TypeError!
  None / 3    # --> TypeError!
  None % 3    # --> TypeError!
  None // 3   # --> TypeError!
  None ** 3   # --> TypeError!

Vergleiche mit dem Wert "None" sind nur für is, is not, == und != möglich:

  None is None       # --> True
  None is True       # --> False
  None is not None   # --> False
  None is not True   # --> True
  None == None       # --> True
  None == True       # --> False
  None != None       # --> False
  None != 123        # --> True

Alle anderen Vergleiche mit "None" generieren IMMER einen "TypeError":

  None < ""      # --> TypeError!
  None > 3.14    # --> TypeError!
  None <= []     # --> TypeError!
  None >= 3+3j   # --> TypeError!

4a) 3-wertige Logik   (Toc)

Die Werte "True", "False" und "None" bilden bgzl. der Operatoren "and", "or"
und "not" eine 3-wertige Logik, wobei "None" bei der Auswertung als "False
zählt und daher als linker Wert von "and" und als rechter Wert von "or"
erhalten bleibt bzw. bei "not" zu True wird (d.h. "and", "or", "not" verhalten
sich UN-SYMMETRISCH, bzw. "and", "or" verhalten sich NICHT KOMMUTATIV).

  * and: True       links --> rechter Wert
         False/None links --> linker Wert
  * or:  True       links --> True
         False/None links --> rechter Wert
  * not: True             --> False
         False/None       --> True

  +-------+-------++-------+-------+-------+-------+
  |   a   |   b   ||  and  |   or  |  not  |   ^   |
  +-------+-------++-------+-------+-------+-------+
  | True  | True  || True  | True  |       | False |
  | True  | False || False | True  | False | True  |
  | True  | None  || None  | True  |       | ERROR*|
  +-------+-------++-------+-------+-------+-------+
  | False | True  || False | True  |       | True  |
  | False | False || False | False | True  | False |
  | False | None  || False | None  |       | ERROR*|
  +-------+-------++-------+-------+-------+-------+
  | None  | True  || None  | True  |       | ERROR*|
  | None  | False || None* | False*| True* | ERROR*|
  | None  | None  || None  | None  |       | ERROR*|
  +-------+-------++-------+-------+-------+-------+
   ERROR = TypeError ausgelöst
   * = Abweichung zu SQL-Logik

4b) 3-wertige SQL-Logik   (Toc)

Die 3-wertige SQL-Logik mit den SQL-Werten TRUE, FALSE und NULL verhält sich
etwas anders (NULL zählt nicht als FALSE, sondern als "undefiniert/ungültig/
unbekannt") und ist somit Ergebnis, falls kein anderweitiges eindeutiges
Ergebnis möglich ist (and, or, not, xor verhalten sich SYMMETRISCH bzw. and,
or, xor verhalten sich KOMMUTATIV).

  * AND: TRUE links         --> rechter Wert
         FALSE links/rechts --> FALSE
         Sonst              --> NULL
  * OR:  FALSE links        --> rechter Wert
         TRUE links/rechts  --> TRUE
         Sonst              --> NULL
  * NOT: TRUE               --> FALSE
         FALSE              --> TRUE
         NULL               --> NULL
  * XOR: Ein NULL           --> NULL
         Zwei TRUE/FALSE    --> FALSE
         Sonst              --> TRUE

  +-------+-------++-------+-------+-------+-------+
  |   a   |   b   ||  and  |  or   |  not  |  xor  |
  +-------+-------++-------+-------+-------+-------+
  | TRUE  | TRUE  || TRUE  | TRUE  |       | FALSE |
  | TRUE  | FALSE || FALSE | TRUE  | FALSE | TRUE  |
  | TRUE  | NULL  || NULL  | TRUE  |       | NULL* |
  +-------+-------++-------+-------+-------+-------+
  | FALSE | TRUE  || FALSE | TRUE  |       | TRUE  |
  | FALSE | FALSE || FALSE | FALSE | TRUE  | FALSE |
  | FALSE | NULL  || FALSE | NULL  |       | NULL* |
  +-------+-------++-------+-------+-------+-------+
  | NULL  | TRUE  || NULL  | TRUE  |       | NULL* |
  | NULL  | FALSE || FALSE*| NULL* | NULL* | NULL* |
  | NULL  | NULL  || NULL  | NULL  |       | NULL* |
  +-------+-------++-------+-------+-------+-------+
  |   a   |   b   ||  and  |  or   |  not  |  xor  |
  +-------+-------++-------+-------+-------+-------+
   * = Abweichung zu Python-Logik