PEP 285 – Hinzufügen eines booleschen Typs
- Autor:
- Guido van Rossum <guido at python.org>
- Status:
- Final
- Typ:
- Standards Track
- Erstellt:
- 08.03.2002
- Python-Version:
- 2.3
- Post-History:
- 08.03.2002, 30.03.2002, 03.04.2002
Zusammenfassung
Diese PEP schlägt die Einführung eines neuen integrierten Typs, bool, mit zwei Konstanten, False und True, vor. Der boolesche Typ wäre eine einfache Unterklasse (in C) des int-Typs, und die Werte False und True würden sich in den meisten Aspekten wie 0 und 1 verhalten (z. B. wäre False==0 und True==1 wahr), außer bei repr() und str(). Alle integrierten Operationen, die konzeptionell ein Boolesches Ergebnis liefern, werden so geändert, dass sie stattdessen False oder True anstelle von 0 oder 1 zurückgeben; dazu gehören beispielsweise Vergleiche, der „not“-Operator und Prädikate wie isinstance().
Überprüfung
Ich habe genug Feedback gesammelt, um mir ein Leben lang davon zu ernähren, daher erkläre ich die Überprüfung offiziell FERTIG. Ich hatte heute chinesisches Essen; mein Glückskeks sagte „Starke und bittere Worte deuten auf eine schwache Sache hin.“ Es erinnerte mich an einige der Beiträge gegen diese PEP… :-)
Auf jeden Fall sind hier meine BDFL-Erklärungen. (Zusammenfassung: Ich ändere nichts; alle Varianten werden abgelehnt.)
- Soll diese PEP angenommen werden?
=> Ja.
Es gab viele Argumente gegen die PEP. Viele davon basierten auf Missverständnissen. Ich habe versucht, einige der häufigsten Missverständnisse unten im Haupttext der PEP zu klären. Das einzige Problem, das für mich überhaupt ins Gewicht fällt, ist die Tendenz von Neulingen, „if x == True“ zu schreiben, wo „if x“ ausreichen würde. Mehr dazu auch unten. Ich denke, das ist kein ausreichender Grund, die PEP abzulehnen.
- Sollte
str(True)„True“ oder „1“ zurückgeben? „1“ könnte Rückwärtskompatibilitätsprobleme reduzieren, sieht aber seltsam aus. (repr(True)würde immer „True“ zurückgeben.)=> „True“.
Fast alle Gutachter stimmen dem zu.
- Sollen die Konstanten ‚True‘ und ‚False‘ (ähnlich wie None) oder ‚true‘ und ‚false‘ (wie in C++, Java und C99) genannt werden?
=> True und False.
Die meisten Gutachter stimmen zu, dass Konsistenz innerhalb von Python wichtiger ist als Konsistenz mit anderen Sprachen.
- Sollen wir uns bemühen, nicht-boolesche Operationen mit booleschen Werten in Zukunft durch geeignete Warnungen zu eliminieren, sodass beispielsweise True+1 schließlich (in Python 3000) illegal wäre?
=> Nein.
Es gibt eine kleine, aber lautstarke Minderheit, die es vorziehen würde, „Lehrbuch“-Boolesche Werte zu sehen, die gar keine arithmetischen Operationen unterstützen, aber die meisten Gutachter stimmen mir zu, dass Boolesche Werte immer arithmetische Operationen zulassen sollten.
- Sollte
operator.truth(x)einen int oder einen bool zurückgeben?=> bool.
Tim Peters glaubt, es sollte einen int zurückgeben, aber fast alle anderen Gutachter stimmen zu, dass es einen bool zurückgeben sollte. Meine Begründung:
operator.truth()existiert, um einen booleschen Kontext auf sein Argument zu erzwingen (es ruft die C-APIPyObject_IsTrue()auf). Ob das Ergebnis als int oder bool gemeldet wird, ist zweitrangig; wenn bool existiert, gibt es keinen Grund, es nicht zu verwenden. (Unter der PEP wirdoperator.truth()jetzt zu einem Alias fürbool(); das ist in Ordnung.) - Soll bool von int erben?
=> Ja.
In einer idealen Welt könnte bool besser als separater Ganzzahltyp implementiert werden, der weiß, wie gemischte arithmetische Operationen durchgeführt werden. Das Erben von bool von int erleichtert die Implementierung jedoch enorm (teilweise, da jeder C-Code, der
PyInt_Check()aufruft, weiterhin funktioniert – dies gibt für Unterklassen von int „true“ zurück). Außerdem glaube ich, dass dies in Bezug auf Austauschbarkeit richtig ist: Code, der einen int benötigt, kann einen bool erhalten und er verhält sich gleich wie 0 oder 1. Code, der einen bool benötigt, funktioniert möglicherweise nicht, wenn er einen int erhält; zum Beispiel ist 3 & 4 gleich 0, aber sowohl 3 als auch 4 sind wahr, wenn sie als Wahrheitswerte betrachtet werden. - Soll der Name ‚bool‘ geändert werden?
=> Nein.
Einige Gutachter haben sich für boolean statt für bool ausgesprochen, da dies leichter zu verstehen wäre (Anfänger haben vielleicht von Boolescher Algebra gehört, verbinden dies aber möglicherweise nicht mit bool) oder weil sie Abkürzungen hassen. Meine Meinung: Python verwendet Abkürzungen wohlüberlegt (wie ‚def‘, ‚int‘, ‚dict‘) und ich glaube nicht, dass diese ein Hindernis für das Verständnis darstellen. Für einen Neuling spielt es keine Rolle, ob es Waffel oder bool genannt wird; es ist ein neues Wort, und sie lernen schnell, was es bedeutet.
Ein Gutachter hat dafür argumentiert, den Namen ‚truth‘ zu machen. Ich finde das einen unattraktiven Namen und würde diesen Begriff (in der Dokumentation) tatsächlich für das abstraktere Konzept der Wahrheitswerte reservieren, das in Python bereits existiert. Zum Beispiel: „wenn ein Container als Wahrheitswert interpretiert wird, ist ein leerer Container falsch und ein nicht-leerer Container ist wahr.“
- Sollen wir uns bemühen, zukünftig zu verlangen, dass Boolesche Operationen (wie „if“, „and“, „not“) ein bool als Argument haben, sodass zum Beispiel „if []:“ illegal würde und als „if bool([]):“ geschrieben werden müsste???
=> Nein!!!
Einige Leute glauben, dass dies das Verhalten einer Sprache mit einem booleschen Lehrbuchtyp sein sollte. Da dies angesprochen wurde, haben andere befürchtet, dass ich dieser Position zustimmen könnte. Lassen Sie mich meine Position dazu ganz klar darlegen. Dies ist nicht Teil der Motivation der PEP und ich beabsichtige nicht, diese Änderung vorzunehmen. (Siehe auch den Abschnitt „Klärung“ unten.)
Begründung
Die meisten Sprachen entwickeln irgendwann einen booleschen Typ; selbst C99 (der neue und verbesserte C-Standard, noch nicht weit verbreitet) hat einen.
Viele Programmierer scheinen das Bedürfnis nach einem booleschen Typ zu haben; die meisten Python-Dokumentationen enthalten eine kleine Entschuldigung für das Fehlen eines booleschen Typs. Ich habe viele Module gesehen, die am Anfang Konstanten wie „False=0“ und „True=1“ (oder ähnliches) definierten und diese verwendeten. Das Problem dabei ist, dass jeder es anders macht. Sollten Sie zum Beispiel „FALSE“, „false“, „False“, „F“ oder sogar „f“ verwenden? Und sollte false der Wert Null oder None sein, oder vielleicht ein Wahrheitswert eines anderen Typs, der als „true“ oder „false“ ausgegeben wird? Das Hinzufügen eines Standard-Booleschen Typs zur Sprache löst diese Probleme.
Einige externe Bibliotheken (wie Datenbanken und RPC-Pakete) müssen zwischen booleschen und ganzzahligen Werten unterscheiden können, und obwohl es normalerweise möglich ist, eine Lösung zu finden, wäre es einfacher, wenn die Sprache einen standardmäßigen booleschen Typ anbieten würde. Dies gilt auch für Jython: Einige Java-Klassen haben separat überladene Methoden oder Konstruktoren für int- und boolesche Argumente. Der boolesche Typ kann verwendet werden, um die boolesche Variante auszuwählen. (Dasselbe gilt offenbar für einige COM-Schnittstellen.)
Der Standard-Boolesche Typ kann auch als Mittel dienen, um einen Wert als Boolesch zu interpretieren, was zur Normalisierung von Booleschen Werten verwendet werden kann. Wenn ein Boolescher Wert auf einen von zwei Werten normalisiert werden muss, ist bool(x) viel klarer als „not not x“ und viel prägnanter als
if x:
return 1
else:
return 0
Hier sind einige Argumente aus dem Unterricht von Python. Wenn ich Leuten Vergleichsoperatoren usw. in der interaktiven Shell zeige, halte ich das für ein wenig hässlich
>>> a = 13
>>> b = 12
>>> a > b
1
>>>
Wenn dies wäre
>>> a > b
True
>>>
würde dies jedes Mal eine Millisekunde weniger Nachdenken erfordern, wenn eine 0 oder 1 ausgegeben würde.
Es gibt auch das Problem (das selbst erfahrene Pythonisten, die schon länger nicht mehr mit der Sprache gearbeitet haben, verwirrt hat), dass, wenn man sieht
>>> cmp(a, b)
1
>>> cmp(a, a)
0
>>>
könnte man versucht sein zu glauben, dass cmp() ebenfalls einen Wahrheitswert zurückgibt, während es in Wirklichkeit drei verschiedene Werte zurückgeben kann (-1, 0, 1). Wenn Ganzzahlen nicht (normalerweise) zur Darstellung von Booleschen Ergebnissen verwendet würden, wäre dies viel klarer als etwas völlig anderes erkennbar.
Spezifikation
Der folgende Python-Code spezifiziert die meisten Eigenschaften des neuen Typs
class bool(int):
def __new__(cls, val=0):
# This constructor always returns an existing instance
if val:
return True
else:
return False
def __repr__(self):
if self:
return "True"
else:
return "False"
__str__ = __repr__
def __and__(self, other):
if isinstance(other, bool):
return bool(int(self) & int(other))
else:
return int.__and__(self, other)
__rand__ = __and__
def __or__(self, other):
if isinstance(other, bool):
return bool(int(self) | int(other))
else:
return int.__or__(self, other)
__ror__ = __or__
def __xor__(self, other):
if isinstance(other, bool):
return bool(int(self) ^ int(other))
else:
return int.__xor__(self, other)
__rxor__ = __xor__
# Bootstrap truth values through sheer willpower
False = int.__new__(bool, 0)
True = int.__new__(bool, 1)
Die Werte False und True werden Singletons sein, wie None. Da der Typ zwei Werte hat, sollten diese vielleicht „Doubletons“ genannt werden? Die tatsächliche Implementierung wird die Erstellung anderer Instanzen von bool nicht zulassen.
True und False werden ordnungsgemäß durch Pickling und Marshaling hin- und herlaufen; zum Beispiel gibt pickle.loads(pickle.dumps(True)) True zurück, und das gilt auch für marshal.loads(marshal.dumps(True)).
Alle integrierten Operationen, die definiert sind, ein boolesches Ergebnis zurückzugeben, werden so geändert, dass sie stattdessen False oder True anstelle von 0 oder 1 zurückgeben. Insbesondere betrifft dies Vergleiche (<, <=, ==, !=, >, >=, is, is not, in, not in), den unären Operator ‚not‘, die integrierten Funktionen callable(), hasattr(), isinstance() und issubclass(), die dict-Methode has_key(), die String- und Unicode-Methoden endswith(), isalnum(), isalpha(), isdigit(), islower(), isspace(), istitle(), isupper() und startswith(), die Unicode-Methoden isdecimal() und isnumeric() und das ‚closed‘-Attribut von Dateiobjekten. Die Prädikate im operator-Modul werden ebenfalls so geändert, dass sie einen bool zurückgeben, einschließlich operator.truth().
Da bool von int erbt, ist True+1 gültig und ergibt 2 und so weiter. Dies ist wichtig für die Rückwärtskompatibilität: Da Vergleiche usw. derzeit ganzzahlige Werte zurückgeben, gibt es keine Möglichkeit zu erkennen, welche bestehenden Anwendungen diese Werte verwenden.
Es wird erwartet, dass die Standardbibliothek im Laufe der Zeit aktualisiert wird, um False und True zu verwenden, wo dies angebracht ist (aber nicht, um einen booleschen Argumenttyp zu verlangen, wo zuvor ein int erlaubt war). Diese Änderung sollte keine zusätzlichen Probleme verursachen und ist durch diese PEP nicht detailliert spezifiziert.
C API
Die Header-Datei „boolobject.h“ definiert die C-API für den booleschen Typ. Sie wird von „Python.h“ eingebunden, sodass sie nicht direkt eingebunden werden muss.
Die bestehenden Namen Py_False und Py_True verweisen auf die eindeutigen booleschen Objekte False und True (zuvor verwiesen sie auf statische int-Objekte mit den Werten 0 und 1, die unter den int-Werten nicht eindeutig waren).
Eine neue API, PyObject *PyBool_FromLong(long), nimmt ein C-long-int-Argument entgegen und gibt eine neue Referenz auf entweder Py_False (wenn das Argument Null ist) oder Py_True (wenn es ungleich Null ist) zurück.
Um zu prüfen, ob ein Objekt ein bool ist, kann das Makro PyBool_Check() verwendet werden.
Der Typ von bool-Instanzen ist PyBoolObject *.
Das boolesche Typobjekt ist als PyBool_Type verfügbar.
Klärung
Diese PEP ändert nicht die Tatsache, dass fast alle Objekttypen als Wahrheitswerte verwendet werden können. Wenn zum Beispiel eine leere Liste in einer if-Anweisung verwendet wird, ist sie falsch, und eine nicht-leere ist wahr; dies ändert sich nicht und es gibt keine Pläne, dies jemals zu ändern.
Das Einzige, was sich ändert, sind die bevorzugten Werte zur Darstellung von Wahrheitswerten, wenn sie explizit zurückgegeben oder zugewiesen werden. Zuvor waren diese bevorzugten Wahrheitswerte 0 und 1; die PEP ändert die bevorzugten Werte in False und True und ändert integrierte Operationen so, dass sie diese bevorzugten Werte zurückgeben.
Kompatibilität
Aufgrund der Rückwärtskompatibilität fehlen dem booleschen Typ viele Eigenschaften, die sich einige wünschen würden. Beispielsweise sind arithmetische Operationen mit einem oder zwei booleschen Argumenten erlaubt, wobei False als 0 und True als 1 behandelt wird. Auch ein bool kann als Sequenzindex verwendet werden.
Ich sehe darin kein Problem und möchte die Sprache auch nicht in diese Richtung weiterentwickeln. Ich glaube nicht, dass eine strengere Auslegung von „Booleschem“ die Sprache verständlicher macht.
Eine weitere Folge der Kompatibilitätsanforderung ist, dass der Ausdruck „True and 6“ den Wert 6 hat und ähnlich der Ausdruck „False or None“ den Wert None hat. Die Operatoren „and“ und „or“ sind nützlicherweise so definiert, dass sie das erste Argument zurückgeben, das das Ergebnis bestimmt, und dies wird sich nicht ändern; insbesondere zwingen sie das Ergebnis nicht dazu, ein bool zu sein. Wenn beide Argumente boolesch sind, ist das Ergebnis natürlich immer boolesch. Es kann auch leicht in einen bool umgewandelt werden, indem man zum Beispiel „bool(x and y)“ schreibt.
Gelöste Probleme
(Siehe auch den Abschnitt „Überprüfung“ oben.)
- Da die
repr()oderstr()eines booleschen Wertes sich von einem int-Wert unterscheidet, können einige Codes (z. B. doctest-basierte Unit-Tests und möglicherweise Datenbankcode, der auf Dinge wie „%s“ % truth angewiesen ist) fehlschlagen. Dies ist leicht zu umgehen (ohne expliziten Verweis auf den booleschen Typ), und es wird erwartet, dass dies nur eine sehr kleine Menge an Code betrifft, der leicht korrigiert werden kann. - Andere Sprachen (C99, C++, Java) nennen die Konstanten „false“ und „true“ in Kleinbuchstaben. Für Python ziehe ich es vor, mich am Beispiel der bestehenden integrierten Konstanten zu orientieren, die alle CapitalizedWords verwenden:
None,Ellipsis,NotImplemented(sowie alle integrierten Ausnahmen). Der integrierte Namensraum von Python verwendet nur Kleinbuchstaben für Funktionen und Typen. - Es wurde vorgeschlagen, um die Erwartungen der Benutzer zu erfüllen, dass für jedes x, das in einem booleschen Kontext als wahr gilt, der Ausdruck
x == Truewahr sein sollte und ebenso, wenn x als falsch gilt,x == Falsewahr sein sollte. Insbesondere Neulinge, die gerade erst etwas über boolesche Variablen gelernt haben, werden wahrscheinlich schreibenif x == True: ...
anstelle der korrekten Form,
if x: ...
Es scheinen starke psychologische und linguistische Gründe dafür zu geben, warum viele Menschen zunächst mit der letzteren Form nicht vertraut sind, aber ich glaube, dass die Lösung in der Bildung liegen sollte und nicht darin, die Sprache zu verkrüppeln. Schließlich wird == im Allgemeinen als transitive Operation angesehen, was bedeutet, dass aus
a==bundb==cwira==cableiten können. Aber wenn irgendein Vergleich mitTrueGleichheit melden würde, wenn der andere Operand ein wahrer Wert irgendeines Typs ist, würden Gräueltaten wie6==True==7wahr sein, wovon man auf die Falschheit6==7schließen könnte. Das ist inakzeptabel. (Außerdem würde es die Rückwärtskompatibilität brechen. Aber selbst wenn es das nicht täte, wäre ich aus den genannten Gründen dagegen.)Neulinge sollten auch daran erinnert werden, dass es niemals einen Grund gibt, Folgendes zu schreiben:
if bool(x): ...
da der bool in „if“ implizit ist. Explizit ist hier nicht besser als implizit, da die zusätzliche Wortwahl die Lesbarkeit beeinträchtigt und keine andere Interpretation möglich ist. Es gibt jedoch manchmal einen Grund, Folgendes zu schreiben:
b = bool(x)
Dies ist nützlich, wenn es unattraktiv ist, eine Referenz auf ein beliebiges Objekt x zu behalten, oder wenn aus irgendeinem anderen Grund eine Normalisierung erforderlich ist. Es ist auch manchmal angebracht, Folgendes zu schreiben:
i = int(bool(x))
was den bool in einen int mit dem Wert 0 oder 1 umwandelt. Dies vermittelt die Absicht, den Wert fortan als int zu verwenden.
Implementierung
Eine vollständige Implementierung in C wurde auf den SourceForge Patch-Manager hochgeladen: https://bugs.python.org/issue528022
Dies wird bald in CVS für python 2.3a0 eingecheckt.
Urheberrecht
Dieses Dokument wurde gemeinfrei erklärt.
Quelle: https://github.com/python/peps/blob/main/peps/pep-0285.rst
Zuletzt geändert: 2025-02-01 08:55:40 GMT