Warum erlaubt Python 3 "00" als Literal für 0, aber nicht "01" als Literal für 1?

111

Warum erlaubt Python 3 "00" als Literal für 0, aber nicht "01" als Literal für 1? Gibt es einen guten Grund? Diese Inkonsistenz verblüfft mich. (Und wir sprechen von Python 3, das absichtlich die Abwärtskompatibilität gebrochen hat, um Ziele wie Konsistenz zu erreichen.)

Beispielsweise:

>>> from datetime import time
>>> time(16, 00)
datetime.time(16, 0)
>>> time(16, 01)
  File "<stdin>", line 1
    time(16, 01)
              ^
SyntaxError: invalid token
>>>
Walross
quelle
42
Es kann jetzt nicht entfernt werden, sonst wird die Abwärtskompatibilität mit dieser Frage unterbrochen!
John La Rooy

Antworten:

103

Per https://docs.python.org/3/reference/lexical_analysis.html#integer-literals :

Ganzzahlige Literale werden durch die folgenden lexikalischen Definitionen beschrieben:

integer        ::=  decimalinteger | octinteger | hexinteger | bininteger
decimalinteger ::=  nonzerodigit digit* | "0"+
nonzerodigit   ::=  "1"..."9"
digit          ::=  "0"..."9"
octinteger     ::=  "0" ("o" | "O") octdigit+
hexinteger     ::=  "0" ("x" | "X") hexdigit+
bininteger     ::=  "0" ("b" | "B") bindigit+
octdigit       ::=  "0"..."7"
hexdigit       ::=  digit | "a"..."f" | "A"..."F"
bindigit       ::=  "0" | "1"

Es gibt keine Begrenzung für die Länge von Ganzzahlliteralen, abgesehen davon, was im verfügbaren Speicher gespeichert werden kann.

Beachten Sie, dass führende Nullen in einer Dezimalzahl ungleich Null nicht zulässig sind. Dies dient zur Disambiguierung mit Oktalliteralen im C-Stil, die Python vor Version 3.0 verwendet hat.

Wie hier erwähnt, sind führende Nullen in einer Nicht-Null- Dezimalzahl nicht zulässig. "0"+ist als ganz besonderer Fall legal, der in Python 2 nicht vorhanden war :

integer        ::=  decimalinteger | octinteger | hexinteger | bininteger
decimalinteger ::=  nonzerodigit digit* | "0"
octinteger     ::=  "0" ("o" | "O") octdigit+ | "0" octdigit+

SVN Commit r55866 hat PEP 3127 im Tokenizer implementiert, wodurch die alten 0<octal>Nummern verboten werden. Seltsamerweise fügt es jedoch auch diesen Hinweis hinzu:

/* in any case, allow '0' as a literal */

mit einem speziellen nonzeroFlag, das nur dann ein a auslöst, SyntaxErrorwenn die folgende Ziffernfolge eine Ziffer ungleich Null enthält.

Dies ist seltsam, da PEP 3127 diesen Fall nicht zulässt:

In diesem PEP wird vorgeschlagen, dass die Möglichkeit, eine Oktalzahl mithilfe einer führenden Null anzugeben, aus der Sprache in Python 3.0 (und dem Python 3.0-Vorschaumodus 2.6) entfernt wird und dass ein SyntaxError immer dann ausgelöst wird, wenn eine führende "0" angezeigt wird unmittelbar gefolgt von einer weiteren Ziffer .

(Hervorhebung von mir)

Die Tatsache, dass mehrere Nullen zulässig sind, verstößt technisch gegen das PEP und wurde von Georg Brandl grundsätzlich als Sonderfall implementiert. Er nahm die entsprechende Dokumentationsänderung vor, um festzustellen, dass dies "0"+ein gültiger Fall war decimalinteger(zuvor unter octinteger).

Wir werden wahrscheinlich nie genau wissen , warum Georg sich für die "0"+Gültigkeit entschieden hat - es kann für immer ein seltsamer Eckfall in Python bleiben.


UPDATE [28. Juli 2015]: Diese Frage führte zu einem lebhaften Diskussionsthread über Python-Ideen, in dem Georg sich einmischte :

Steven D'Aprano schrieb:

Warum wurde es so definiert? [...] Warum sollten wir 0000 schreiben, um Null zu bekommen?

Ich könnte es dir sagen, aber dann müsste ich dich töten.

Georg

Später brachte der Thread diesen Fehlerbericht hervor , um diesen Sonderfall loszuwerden. Hier sagt Georg :

Ich erinnere mich nicht an den Grund für diese absichtliche Änderung (wie aus der Änderung der Dokumente hervorgeht).

Ich kann jetzt keinen guten Grund für diese Änderung finden [...]

und so haben wir es: Der genaue Grund für diese Inkonsistenz geht mit der Zeit verloren.

Beachten Sie schließlich, dass der Fehlerbericht abgelehnt wurde: Führende Nullen werden für den Rest von Python 3.x weiterhin nur mit Ganzzahlen von Null akzeptiert.

nneonneo
quelle
6
Warum sagst du "Wir werden wahrscheinlich nie genau wissen, warum Georg sich entschieden hat ..."? Wenn jemand, der ihn kennt, diesen Thread sieht und ihn darüber informiert, könnte er kommen und seine Antwort geben! (es sei denn, Sie wissen, dass er sich für immer weigert, seine frühere Python-Arbeit oder einen ähnlichen Umstand zu besprechen)
Walross
1
Ich verstehe nicht, warum sie nicht gerade den zweiten Python 2- octintegerFall gemacht haben "0" octdigit*. 0ist ein oktales Literal in C / C ++.
Random832
1
Eigentlich ist Englisch in dieser Hinsicht etwas mehrdeutig. Das Wort "ein anderer" kann "ein weiterer" oder "ein anderer" bedeuten. Eine gültige englische Interpretation des fettgedruckten Zitats aus PEP 3127 lautet: "Ein SyntaxError wird ausgelöst, wenn auf eine führende '0' unmittelbar eine andere Ziffer als '0' folgt." Ich bin nicht sicher, ob dies tatsächlich beabsichtigt war ( Diese Interpretation scheint zwar vom eigentlichen Code unterstützt zu werden, aber ich glaube auf keinen Fall, dass es richtig ist zu sagen, dass das PEP ohne zusätzliche Klarstellung dieses Satzes technisch verletzt wird.
GrandOpener
2
@GrandOpener: Beachten Sie, dass dies 001illegal ist, während Ihre Interpretation dies legal machen würde (da die Bedeutung von "sofort" ziemlich eindeutig sein sollte).
Nneonneo
Guter Punkt. Das PEP ist also definitiv verletzt; Was nicht eindeutig ist, ist die genaue Art, in der es verletzt wird. :)
GrandOpener
17

Es ist ein Sonderfall ( "0"+)

2.4.4. Ganzzahlige Literale

Ganzzahlige Literale werden durch die folgenden lexikalischen Definitionen beschrieben:

Ganzzahl :: = Dezimalzahl | Oktinteger | hexinteger | bininteger
decimalinteger :: = Ziffer ohne Ziffern * | "0" +
nonzerodigit :: = "1" ... "9"
Ziffer :: = "0" ... "9"
octinteger :: = "0" ("o" | "O") octdigit +
hexinteger :: = "0" ("x" | "X") hexdigit +
bininteger :: = "0" ("b" | "B") bindigit +
octdigit :: = "0" ... "7"
hexdigit :: = digit | "a" ... "f" | "A" ... "F"
bindigit :: = "0" | "1"

Wenn Sie sich die Grammatik ansehen, ist es leicht zu erkennen, 0dass ein Sonderfall erforderlich ist. Ich bin mir nicht sicher, warum das ' +' dort als notwendig erachtet wird. Zeit, die Entwickler-Mailingliste zu durchsuchen ...


Interessant zu bemerken, dass in Python2 mehr als eine 0als analysiert wurde octinteger(das Endergebnis ist jedoch immer noch 0)

decimalinteger :: = Ziffer ohne Ziffern * | "0"
octinteger :: = "0" ("o" | "O") octdigit + | "0" Oktdigit +
John La Rooy
quelle
1
Und eine Idee, warum es gibt "0"+und nicht "0"?
Lejlot
1
@lejlot, noch nicht - aber ich bin fasziniert. Es ist definitiv Teil der Spezifikation
John La Rooy
3

Python2 verwendete die führende Null, um Oktalzahlen anzugeben:

>>> 010
8

Um dies zu vermeiden (? Irreführend) Verhalten erfordert Python3 explizite Präfixe 0b, 0o, 0x:

>>> 0o10
8
dlask
quelle
15
Die Frage bleibt: Warum ist 00erlaubt? (Und 000, 0000etc.)
Michael Geary
4
@MichaelGeary: Möglicherweise, weil es nicht mehrdeutig sein kann (00000000 ist 0, unabhängig von der Basis) und das Entfernen würde den Code unnötig beschädigen? Immer noch seltsam.
RemcoGerlich
5
@RemcoGerlich Wenn ich mich nicht irre, 01ist das auch 1unabhängig von der Basis.
Holt
2
@Holt: aber "0" + "1" zulassen? als Sonderfall wäre das wohl noch verwirrender.
RemcoGerlich
4
@RemcoGerlich Nie gesagt, es würde nicht;) Ich habe nur gesagt, dass das can't be ambiguouskein Argument ist, da es 01auch nicht mehrdeutig sein kann. IMO, der 00Fall ist nur ein Sonderfall, weil er 0nicht sein sollte.
Holt