Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python Enhancement Proposals

PEP 3127 – Unterstützung und Syntax für Ganzzahl-Literale

Autor:
Patrick Maupin <pmaupin at gmail.com>
Discussions-To:
Python-3000 Liste
Status:
Final
Typ:
Standards Track
Erstellt:
14. Mär 2007
Python-Version:
3.0
Post-History:
18. Mär 2007

Inhaltsverzeichnis

Zusammenfassung

Dieses PEP schlägt Änderungen am Python-Kern vor, um die Behandlung von String-Literalen für ganze Zahlen in verschiedenen Radizes (Basen) zu rationalisieren. Diese Änderungen sind für Python 3.0 vorgesehen, aber die abwärtskompatiblen Teile der Änderungen sollten zu Python 2.6 hinzugefügt werden, so dass alle gültigen Ganzzahl-Literale von 3.0 auch in 2.6 gültig sind.

Der Vorschlag ist, dass

  1. Oktal-Literale nun mit einem führenden "0o" oder "0O" anstelle von "0" angegeben werden müssen;
  2. Binär-Literale jetzt über ein führendes "0b" oder "0B" unterstützt werden; und
  3. für Binärzahlen in der String-Formatierung gesorgt wird.

Motivation

Dieses PEP wurde durch zwei verschiedene Probleme motiviert:

  • Die Standard-Oktaldarstellung von ganzen Zahlen ist für Personen, die mit C-ähnlichen Sprachen nicht vertraut sind, stillschweigend verwirrend. Es ist äußerst einfach, versehentlich ein Ganzzahl-Objekt mit dem falschen Wert zu erstellen, da '013' für die Python-Sprache selbst 'Dezimal 11' und nicht 'Dezimal 13' bedeutet, was nicht die Bedeutung ist, die die meisten Menschen diesem Literal zuweisen würden.
  • Einige Python-Benutzer haben einen starken Wunsch nach Binärunterstützung in der Sprache.

Spezifikation

Grammatik-Spezifikation

Die Grammatik wird geändert. Für Python 2.6 sind die geänderten und neuen Token-Definitionen:

integer        ::=     decimalinteger | octinteger | hexinteger |
                       bininteger | oldoctinteger

octinteger     ::=     "0" ("o" | "O") octdigit+

bininteger     ::=     "0" ("b" | "B") bindigit+

oldoctinteger  ::=     "0" octdigit+

bindigit       ::=     "0" | "1"

Für Python 3.0 wird "oldoctinteger" nicht mehr unterstützt, und es wird eine Ausnahme ausgelöst, wenn ein Literal mit einer führenden "0" und einem zweiten Zeichen, das eine Ziffer ist, beginnt.

Für beide Versionen erfordert dies Änderungen an PyLong_FromString sowie an der Grammatik.

Die Dokumentation muss ebenfalls geändert werden: grammar.txt, sowie der Abschnitt über Ganzzahl-Literale im Referenzhandbuch.

PEP 306 sollte auf andere Probleme überprüft werden, und dieses PEP sollte aktualisiert werden, wenn das darin beschriebene Verfahren unzureichend ist.

int()-Spezifikation

int(s, 0) wird ebenfalls mit der neuen Grammatikdefinition übereinstimmen.

Dies sollte automatisch mit den Änderungen an PyLong_FromString geschehen, die für die Grammatikänderung erforderlich sind.

Auch die Dokumentation für int() sollte geändert werden, um zu erklären, dass int(s) identisch mit int(s, 10) funktioniert, und das Wort "raten" sollte aus der Beschreibung von int(s, 0) entfernt werden.

long()-Spezifikation

Für Python 2.6 sollte die Implementierung und Dokumentation von long() geändert werden, um die neue Grammatik widerzuspiegeln.

Tokenizer-Ausnahmebehandlung

Wenn ein ungültiges Token eine führende "0" enthält, sollte die Fehlermeldung informativer sein als das aktuelle "SyntaxError: invalid token". Sie sollte erklären, dass Dezimalzahlen keine führende Null haben dürfen und dass Oktalzahlen ein "o" nach der führenden Null erfordern.

int()-Ausnahmebehandlung

Der ValueError, der bei jedem Aufruf von int() mit einem String ausgelöst wird, sollte zumindest explizit die Basis in der Fehlermeldung enthalten, z.B.

ValueError: invalid literal for base 8 int(): 09

oct()-Funktion

oct() sollte aktualisiert werden, um '0o' vor den Oktalziffern auszugeben (für 3.0 und den Kompatibilitätsmodus von 2.6).

Ausgabeformatierung

In 3.0 muss die alternative Syntax des String %-Operators für die Option 'o' aktualisiert werden, um '0o' voranzustellen, anstatt '0'. In 2.6 fügt die alternative Oktalformatierung weiterhin nur '0' hinzu. Weder in 2.6 noch in 3.0 unterstützt der %-Operator die Binärausgabe. Dies liegt daran, dass die Binärausgabe bereits von PEP 3101 (str.format) unterstützt wird, welche die bevorzugte String-Formatierungsmethode ist.

Übergang von 2.6 zu 3.0

Der 2to3-Übersetzer muss 'o' in jedes Oktal-String-Literal einfügen.

Die Py3K-kompatible Option für Python 2.6 sollte dazu führen, dass Versuche, oldoctinteger-Literale zu verwenden, eine Ausnahme auslösen.

Begründung

Die meiste Diskussion über diese Themen fand auf der Python-3000-Mailingliste ab dem 14. März 2007 statt, angestoßen durch die Beobachtung, dass der durchschnittliche Mensch völlig verwirrt wäre, wenn er feststellen würde, dass das Voranstellen einer "0" vor eine Ziffernfolge die Bedeutung dieser Ziffernfolge völlig ändert.

Es wurde während dieser Diskussion darauf hingewiesen, dass eine ähnliche, aber kürzere Diskussion zu diesem Thema im Januar 2006 stattfand, angestoßen durch die Entdeckung desselben Problems.

Hintergrund

Aus historischen Gründen leiht sich Pythons String-Darstellung von ganzen Zahlen in verschiedenen Basen (Radizes) für die String-Formatierung und Token-Literale stark von C. [1] [2] Die Praxis hat gezeigt, dass die historische Methode zur Angabe einer Oktalzahl verwirrend ist und dass es auch schön wäre, zusätzliche Unterstützung für Binär-Literale zu haben.

In diesem Dokument beziehen sich, sofern nicht anders angegeben, Diskussionen über die String-Darstellung von ganzen Zahlen auf folgende Funktionen:

  • Literale Ganzzahl-Token, wie sie bei normaler Modulkompilierung, durch eval() und durch int(token, 0) verwendet werden. (int(token) und int(token, 2-36) werden durch diesen Vorschlag nicht geändert.)
    • Unter 2.6 wird long() wie int() behandelt.
  • Formatierung von ganzen Zahlen in Strings, entweder über den %-String-Operator oder die neue PEP 3101 Methode für erweiterte String-Formatierung.

Es wird angenommen, dass

  • Alle diese Funktionen aus Konsistenzgründen einen identischen Satz von unterstützten Radizes haben sollten.
  • Die Python-Quellcode-Syntax und int(mystring, 0) sollten weiterhin identisches Verhalten aufweisen.

Entfernung der alten Oktal-Syntax

Dieses PEP schlägt vor, dass die Möglichkeit, eine Oktalzahl durch eine führende Null anzugeben, in Python 3.0 (und im Python 3.0 Vorschau-Modus von 2.6) aus der Sprache entfernt wird und dass eine SyntaxError ausgelöst wird, wenn eine führende "0" sofort von einer anderen Ziffer gefolgt wird.

Während der gegenwärtigen Diskussion war man sich fast universell einig, dass

eval('010') == 8

nicht mehr wahr sein sollte, weil das für neue Benutzer verwirrend ist. Es wurde auch vorgeschlagen, dass

eval('0010') == 10

wahr werden sollte, aber das ist viel umstrittener, weil es so inkonsistent mit der Verwendung in anderen Computersprachen ist, dass wahrscheinlich Fehler gemacht werden.

Fast alle derzeit populären Computersprachen, einschließlich C/C++, Java, Perl und JavaScript, behandeln eine Ziffernfolge mit einer führenden Null als Oktalzahl. Befürworter der Behandlung dieser Zahlen als Dezimalzahlen haben einen sehr gültigen Punkt – wie in Unterstützte Radizes unten erläutert, verwendet die gesamte nicht-computerbezogene Welt fast ausschließlich Dezimalzahlen. Es gibt zahlreiche anekdotische Beweise dafür, dass viele Menschen enttäuscht und verwirrt sind, wenn sie mit nicht-dezimalen Radizes konfrontiert werden.

In den meisten Fällen schreiben die meisten Leute jedoch keine unnötigen Nullen vor ihre Dezimalzahlen. Die wichtigste Ausnahme ist, wenn versucht wird, Zahlenspalten auszurichten. Da jedoch PEP 8 die Verwendung von Leerzeichen zur Ausrichtung von Python-Code ausdrücklich missbilligt, würde man vermuten, dass das gleiche Argument für die Verwendung führender Nullen für denselben Zweck gilt.

Schließlich ist, obwohl sich die E-Mail-Diskussion oft darauf konzentrierte, ob überhaupt noch jemand Oktalzahlen verwendet und ob wir uns trotzdem um diese "Old-Timer" kümmern sollten, das fast vollständig nebensächlich.

Nehmen wir an, der seltene absolute Computer-Neuling, der entweder gelegentlich oder aus Gewohnheit führende Nullen für Dezimalzahlen verwendet. Python könnte entweder:

  1. stillschweigend das Falsche mit ihren Zahlen tun, wie es jetzt geschieht;
  2. sie sofort von der Vorstellung abbringen, dass dies eine gültige Syntax ist (und ja, die SyntaxWarning sollte sanfter sein als sie derzeit ist, aber das ist ein Thema für ein anderes PEP); oder
  3. sie weiterhin glauben lassen, dass Computer mit mehrstelligen Dezimalzahlen, die mit "0" beginnen, zufrieden sind.

Manche Leute glauben leidenschaftlich, dass (c) die richtige Antwort ist, und sie hätten absolut Recht, wenn wir sicher sein könnten, dass neue Benutzer niemals aufblühen und wachsen und AJAX-Anwendungen schreiben werden.

So kann es sein, dass ein neuer Python-Benutzer (derzeit) verwirrt ist über die verzögerte Entdeckung, dass seine Zahlen nicht richtig funktionieren. Wir können dies beheben, indem wir ihm sofort erklären, dass Python keine führenden Nullen mag (hoffentlich mit einer angemessenen Meldung!), oder wir können diese Lernerfahrung an den JavaScript-Interpreter im Browser delegieren und ihn versuchen lassen, sein Problem dort zu debuggen.

Unterstützte Radizes

Dieses PEP schlägt vor, dass die unterstützten Radizes für die Python-Sprache 2, 8, 10 und 16 sein werden.

Sobald vereinbart ist, dass die alte Syntax für die Oktal-Darstellung von ganzen Zahlen (Radix 8) aus der Sprache entfernt werden muss, stellt sich die nächste offensichtliche Frage: "Brauchen wir überhaupt eine Möglichkeit, Zahlen in Oktal anzugeben (und anzuzeigen)?"

Auf diese Frage folgt schnell: "Welche Radizes muss die Sprache unterstützen?" Da Computer so gut darin sind, das zu tun, was man ihnen sagt, war eine verlockende Antwort in der Diskussion: "alle von ihnen." Diese Antwort wurde offensichtlich schon früher gegeben – der int()-Konstruktor akzeptiert eine explizite Radix mit einem Wert zwischen 2 und 36, einschließlich, wobei die letztere Zahl eine verdächtige arithmetische Ähnlichkeit zur Summe der Anzahl der Ziffern und der Anzahl der gleichnamigen Buchstaben im ASCII-Alphabet aufweist.

Aber das beste Argument für die Aufnahme wird einen Anwendungsfall haben, der es unterstützt, so dass die Idee, alle Radizes zu unterstützen, schnell verworfen wurde und die einzigen Radizes, die noch wirkliche Unterstützung hatten, Dezimal, Hexadezimal, Oktal und Binär waren.

Nur weil eine bestimmte Radix einen lautstarken Unterstützer auf der Mailingliste hat, bedeutet das nicht, dass sie wirklich in der Sprache sein sollte, daher ist der Rest dieses Abschnitts eine Abhandlung über den Nutzen dieser spezifischen Radizes im Vergleich zu anderen möglichen Wahlen.

Menschen verwenden ständig andere numerische Basen. Wenn ich Ihnen sage, dass es 12:30 Uhr ist, habe ich quantitative Informationen übermittelt, die angeblich aus *drei* separaten Basen (12, 60 und 2) bestehen, von denen nur eine in der oben "vereinbarten" Liste steht. Aber die *Kommunikation* dieser Informationen verwendete zwei Dezimalziffern sowohl für die Basis 12 als auch für die Basis 60 Informationen und, verquert, zwei Buchstaben für Informationen, die in eine einzige Dezimalziffer gepasst hätten.

Im Allgemeinen kommunizieren Menschen "normale" (nicht-computerbezogene) numerische Informationen entweder über Namen (AM, PM, Januar, ...) oder über die Verwendung der Dezimalnotation. Offensichtlich werden Namen selten für große Mengen von Elementen verwendet, so dass die Dezimalnotation für alles andere verwendet wird. Es gibt Studien, die versuchen zu erklären, warum das so ist, und die typischerweise zu der erwarteten Schlussfolgerung gelangen, dass das arabische Zahlensystem gut für die menschliche Kognition geeignet ist. [3]

Es gibt sogar Unterstützung in der Geschichte des Designs von Computern, die darauf hindeutet, dass die Dezimalnotation der richtige Weg für Computer ist, um mit Menschen zu kommunizieren. Einer der ersten modernen Computer, ENIAC [4], rechnete in Dezimal, obwohl es bereits existierende Computer gab, die in Binär arbeiteten.

Die Dezimal-Computeroperation war wichtig genug, dass viele Computer, einschließlich des allgegenwärtigen PCs, Anweisungen haben, die für die Operation auf "binär kodierten Dezimalzahlen" (BCD) [5] entwickelt wurden, einer Darstellung, die 4 Bits für jede Dezimalziffer verwendet. Diese Anweisungen stammen aus einer Zeit, in der die anstrengendsten Berechnungen, die je mit vielen Zahlen durchgeführt wurden, die tatsächlichen Berechnungen für die textliche E/A damit waren. Es ist möglich, BCD anzuzeigen, ohne eine Divisions-/Restoperation für jede angezeigte Ziffer durchführen zu müssen, und das war ein riesiger rechnerischer Gewinn, als die meisten Hardware keine schnelle Divisionsmöglichkeit hatten. Ein weiterer Faktor, der zur Verwendung von BCD beitrug, ist, dass mit BCD-Berechnungen das Runden genauso passiert, wie ein Mensch es tun würde, daher wird BCD in Bereichen wie der Finanzwelt immer noch manchmal verwendet, trotz der rechnerischen und speicherbezogenen Überlegenheit von Binär.

Wenn es also nicht die Tatsache gäbe, dass Computer selbst normalerweise Binär für effiziente Berechnung und Datenspeicherung verwenden, wären String-Darstellungen von ganzen Zahlen wahrscheinlich immer in Dezimal.

Leider denkt Computerhardware nicht wie Menschen, daher müssen sich Programmierer und Hardware-Ingenieure oft wie der Computer denken, was bedeutet, dass es für Python wichtig ist, die Fähigkeit zu haben, Binärdaten in einer für Menschen verständlichen Form zu kommunizieren.

Die Anforderung, dass die Binärdaten-Notation kognitiv leicht für Menschen zu verarbeiten sein muss, bedeutet, dass sie eine integrale Anzahl von Binärziffern (Bits) pro Symbol enthalten sollte, während sie ansonsten recht eng der Standard-bewährten Dezimalnotation (Position zeigt Potenz an, größere Magnitude links, nicht zu viele Symbole im Alphabet, etc.) entspricht.

Der offensichtliche "Sweet Spot" für diese Binärdaten-Notation ist somit Oktal, das die größte integrale Anzahl von Bits pro Symbol aus dem arabischen Ziffernalphabet packt.

Tatsächlich wurden einige Computerarchitekturen, wie der PDP8 und der 8080/Z80, in Bezug auf Oktal definiert, im Sinne der Anordnung der Bitfelder von Anweisungen in Dreiergruppen und der Verwendung von Oktaldarstellungen zur Beschreibung des Befehlssatzes.

Auch heute noch ist Oktal wichtig wegen Bit-gepackter Strukturen, die 3 Bits pro Feld bestehen, wie z. B. Unix-Dateiberechtigungsm hooks.

Aber Oktal hat einen Nachteil bei größeren Zahlen. Die Anzahl der Bits pro Symbol ist zwar integral, aber keine Zweierpotenz. Diese Einschränkung (da die Wortbreite der meisten Computer heutzutage eine Zweierpotenz ist) hat zu Hexadezimal geführt, das beliebter ist als Oktal, obwohl es ein um 60 % größeres Alphabet als Dezimal erfordert, da jedes Symbol 4 Bits enthält.

Einige Zahlen, wie Unix-Dateiberechtigungsm hooks, sind für Menschen in Oktal leicht zu dekodieren, aber in Hexadezimal schwer zu dekodieren, während andere Zahlen für Menschen in Hexadezimal viel einfacher zu handhaben sind.

Leider gibt es auch Binärzahlen, die in Computern verwendet werden und die weder in Hexadezimal noch in Oktal sehr gut kommuniziert werden. Glücklicherweise müssen sich weniger Leute regelmäßig damit beschäftigen, aber andererseits bedeutet das, dass mehrere Leute auf der Diskussionsliste die Weisheit in Frage stellten, eine reine Binärdarstellung zu Python hinzuzufügen.

Ein Beispiel, wo diese Zahlen sehr nützlich sind, ist das Lesen und Schreiben von Hardware-Registern. Manchmal verzichten Hardware-Designer auf menschliche Lesbarkeit und entscheiden sich für die Effizienz des Adressraums, indem sie mehrere Bitfelder in einem einzigen Hardware-Register an nicht ausgerichteten Bit-Positionen packen, und es ist mühsam und fehleranfällig für einen Menschen, ein 5-Bit-Feld zu rekonstruieren, das aus den oberen 3 Bits einer Hexadezimalziffer und den unteren 2 Bits der nächsten Hexadezimalziffer besteht.

Selbst wenn die Fähigkeit von Python, Binärinformationen an Menschen zu kommunizieren, nur für einen kleinen technischen Teil der Bevölkerung nützlich ist, gehört genau dieser Teil der Bevölkerung zu den meisten, wenn nicht allen Mitgliedern des Python-Kernteams, so dass selbst reines Binär, die am wenigsten nützliche dieser Notationen, mehrere enthusiastische Unterstützer und wenige, wenn überhaupt, überzeugte Gegner in der Python-Community hat.

Syntax für unterstützte Radizes

Dieser Vorschlag sieht die Verwendung eines "0o"-Präfixes mit einem Klein- oder Großbuchstaben "o" für Oktal und eines "0b"-Präfixes mit einem Klein- oder Großbuchstaben "b" für Binär vor.

Es gab starke Unterstützung dafür, Großbuchstaben nicht zu unterstützen, aber das ist ein separates Thema für ein anderes PEP, da 'j' für komplexe Zahlen, 'e' für Exponent und 'r' für Raw-String (um nur einige zu nennen) bereits Großbuchstaben unterstützen.

Die Syntax zur Abgrenzung der verschiedenen Radizes erhielt in der Diskussion auf Python-3000 viel Aufmerksamkeit. Es gibt mehrere (manchmal widersprüchliche) Anforderungen und "Nice-to-haves" für diese Syntax:

  • Sie sollte so kompatibel wie vernünftig mit anderen Sprachen und früheren Versionen von Python sein, sowohl für die Eingabesyntax als auch für die Ausgabesyntax (z. B. String %-Operator).
  • Sie sollte für den beiläufigen Betrachter so offensichtlich wie möglich sein.
  • Sie sollte es ermöglichen, ganzzahlige Zahlen, die in verschiedenen Basen formatiert sind, visuell zu unterscheiden.

Vorgeschlagene Syntaxen beinhalteten Dinge wie beliebige Radix-Präfixe, wie 16r100 (256 in Hexadezimal), und Radix-Suffixe, ähnlich dem Assembler-Stil-Suffix 100h. Die Debatte, ob der Buchstabe "O" für Oktal verwendet werden könnte, war intensiv – ein Großbuchstabe "O" sieht in manchen Schriftarten verdächtig ähnlich wie eine Null aus. Es wurden Vorschläge gemacht, ein "c" (der zweite Buchstabe von "oCtal") oder sogar ein "t" für "ocTal" und ein "n" für "biNary" zu verwenden, um "x" für "heXadecimal" zu begleiten.

Für den String %-Operator wurde "o" bereits zur Bezeichnung von Oktal verwendet. Die Binärformatierung wird nicht zum %-Operator hinzugefügt, da PEP 3101 (Advanced String Formatting) bereits Binär unterstützt, und die %-Formatierung wird in Zukunft als veraltet gelten.

Am Ende wurde entschieden, dass diese Präfixe nur Kleinbuchstaben verwenden sollten, da ein Großbuchstabe "O" wie eine Null und ein Großbuchstabe "B" wie eine 8 aussehen kann. Aber wie bei 'r' für Raw-Strings kann dies eine Präferenz- oder Stilfragen sein.

Offene Fragen

Es wurde in der Diskussion vorgeschlagen, dass Kleinbuchstaben für alle numerischen und String-Spezialmodifikatoren verwendet werden sollten, wie 'x' für Hexadezimal, 'r' für Raw-Strings, 'e' für Exponentiation und 'j' für komplexe Zahlen. Dies ist ein Thema für ein separates PEP.

Dieses PEP nimmt keine Stellung zu Groß- oder Kleinschreibung für die Eingabe, stellt lediglich fest, dass zur Konsistenz, wenn Großbuchstaben nicht aus der Eingabeparsierung für andere Buchstaben entfernt werden sollen, sie für Oktal und Binär hinzugefügt werden sollten, und dokumentiert die Änderungen unter dieser Annahme, da es noch kein PEP zur Groß-/Kleinschreibungsfrage gibt.

Die Ausgabeformatierung könnte eine andere Geschichte sein – es gibt bereits reichlich Präzedenzfälle für Groß-/Kleinschreibung in der Ausgabeformatzeichenkette, und es müsste ein Konsens darüber bestehen, dass es einen gültigen Anwendungsfall für die "alternative Form" des String %-Operators gibt, um Großbuchstaben 'B' oder 'O' für Binär- oder Oktalausgabe zu unterstützen. Derzeit unterstützt PEP 3101 nicht einmal diese alternative Fähigkeit, und die hex()-Funktion erlaubt dem Programmierer nicht, die Groß-/Kleinschreibung des 'x'-Zeichens anzugeben.

Es gibt immer noch starke Gefühle, dass '0123' in Python 3.0 als Dezimal-Literal erlaubt sein sollte. Wenn dies das Richtige ist, kann dies leicht in einem zusätzlichen PEP behandelt werden. Dieser Vorschlag macht nur den ersten Schritt, '0123' nicht als gültige Oktalzahl zu behandeln, aus den im Rationale erläuterten Gründen.

Gibt es (oder sollte es geben) eine Option für den 2to3-Übersetzer, die nur die 2.6-kompatiblen Änderungen vornimmt? Sollte dies auf 2.6-Bibliothekscode vor der 2.6-Version ausgeführt werden?

Sollte eine bin()-Funktion hinzugefügt werden, die hex() und oct() entspricht?

Ist hex() wirklich so nützlich, wenn wir erweiterte String-Formatierung haben?

Referenzen


Quelle: https://github.com/python/peps/blob/main/peps/pep-3127.rst

Zuletzt geändert: 2025-02-01 08:59:27 GMT