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.
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
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!)
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): ...
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'
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__
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'}
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.
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 |
+-----+-------------+
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 |
+------------+-----------------------------------------------------------+
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
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)
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)
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
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.
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.
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
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!
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!
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
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