Perché Python 3 consente "00" come letterale per 0 ma non consente "01" come letterale per 1?


111

Perché Python 3 consente "00" come letterale per 0 ma non consente "01" come letterale per 1? C'è una buona ragione? Questa incoerenza mi lascia perplesso. (E stiamo parlando di Python 3, che ha intenzionalmente rotto la compatibilità con le versioni precedenti per raggiungere obiettivi come la coerenza.)

Per esempio:

>>> from datetime import time
>>> time(16, 00)
datetime.time(16, 0)
>>> time(16, 01)
  File "<stdin>", line 1
    time(16, 01)
              ^
SyntaxError: invalid token
>>>

42
Non può essere rimosso ora o interromperà la retrocompatibilità con questa domanda!
John La Rooy

Risposte:


103

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

I letterali interi sono descritti dalle seguenti definizioni lessicali:

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"

Non vi è alcun limite per la lunghezza dei valori letterali interi a parte ciò che può essere archiviato nella memoria disponibile.

Notare che gli zeri iniziali in un numero decimale diverso da zero non sono consentiti. Questo serve per disambiguare i letterali ottali in stile C, che Python usava prima della versione 3.0.

Come indicato qui, gli zeri iniziali in un numero decimale diverso da zero non sono consentiti. "0"+è legale come un caso molto speciale, che non era presente in Python 2 :

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

Il commit SVN r55866 ha implementato PEP 3127 nel tokenizer, che vieta i vecchi 0<octal>numeri. Tuttavia, curiosamente, aggiunge anche questa nota:

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

con un nonzeroflag speciale che genera un solo SyntaxErrorse la seguente sequenza di cifre contiene una cifra diversa da zero.

Questo è strano perché PEP 3127 non consente questo caso:

Questo PEP propone che la capacità di specificare un numero ottale utilizzando uno zero iniziale sarà rimossa dal linguaggio in Python 3.0 (e la modalità di anteprima di Python 3.0 2.6), e che un SyntaxError sarà sollevato ogni volta che uno "0" iniziale è immediatamente seguito da un'altra cifra .

(enfasi mia)

Quindi, il fatto che siano consentiti più zeri sta tecnicamente violando il PEP, ed è stato sostanzialmente implementato come un caso speciale da Georg Brandl. Ha apportato la modifica alla documentazione corrispondente per notare che "0"+era un caso valido per decimalinteger(in precedenza che era stato trattato sotto octinteger).

Probabilmente non sapremo mai esattamente perché Georg ha scelto di rendere "0"+valido - potrebbe rimanere per sempre uno strano caso d'angolo in Python.


AGGIORNAMENTO [28 luglio 2015]: Questa domanda ha portato a un vivace thread di discussione sulle idee-pitone in cui Georg è intervenuto :

Steven D'Aprano ha scritto:

Perché è stato definito in questo modo? [...] Perché dovremmo scrivere 0000 per ottenere zero?

Potrei dirtelo, ma poi dovrei ucciderti.

Georg

Successivamente, il thread ha generato questa segnalazione di bug con l' obiettivo di sbarazzarsi di questo caso speciale. Qui, Georg dice :

Non ricordo il motivo di questo cambiamento deliberato (come si vede dalla modifica dei documenti).

Non sono in grado di trovare una buona ragione per questo cambiamento ora [...]

e così l'abbiamo: il motivo preciso dietro questa incoerenza si perde nel tempo.

Infine, nota che la segnalazione di bug è stata rifiutata: gli zeri iniziali continueranno ad essere accettati solo su zero interi per il resto di Python 3.x.


6
Perché dici "Probabilmente non sapremo mai esattamente perché Georg ha scelto di ..."? Se qualcuno che lo conosce vede questo thread e lo informa, allora potrebbe venire a dare la sua risposta! (a meno che tu non sappia che rifiuta per sempre di discutere il suo lavoro passato su Python, o qualche circostanza simile)
walrus

1
Non capisco perché non abbiano realizzato solo il secondo octintegercaso Python 2 "0" octdigit*. 0è un letterale ottale in C / C ++.
Random 832

1
In realtà l'inglese è un po 'ambiguo a questo riguardo. La parola "un altro" può significare "uno in più" o può significare "uno diverso". Un'interpretazione inglese valida della citazione in grassetto da PEP 3127 significa "un SyntaxError verrà sollevato ogni volta che uno '0' iniziale è immediatamente seguito da una cifra diversa da '0'" Non sono sicuro che sia ciò che era effettivamente inteso ( anche se tale interpretazione sembra essere supportata dal codice vero e proprio), ma in ogni caso non credo sia corretto affermare che il PEP è tecnicamente violato senza ulteriori chiarimenti su quella frase.
GrandOpener

2
@GrandOpener: nota che 001è illegale, mentre la tua interpretazione lo renderebbe legale (poiché il significato di "immediatamente" dovrebbe essere abbastanza inequivocabile).
nneonneo

Buon punto. Quindi il PEP è definitivamente violato; ciò che è ambiguo è l'esatta natura in cui viene violato. :)
GrandOpener

17

È un caso speciale ( "0"+)

2.4.4. Letterali interi

I letterali interi sono descritti dalle seguenti definizioni lessicali:

intero :: = decimalinteger | ottinteger | hexinteger | bininteger
decimalinteger :: = nonzerodigit cifra * | "0" +
nonzerodigit :: = "1" ... "9"
cifra :: = "0" ... "9"
ottinteger :: = "0" ("o" | "O") octdigit +
hexinteger :: = "0" ("x" | "X") cifra esadecimale +
bininteger :: = "0" ("b" | "B") bindigit +
octdigit :: = "0" ... "7"
cifra esadecimale :: = cifra | "a" ... "f" | "A" ... "F"
bindigit :: = "0" | "1"

Se guardi la grammatica, è facile vedere che 0serve un caso speciale. Non sono sicuro del motivo per cui " +" è considerato necessario lì. È ora di scavare nella mailing list degli sviluppatori ...


È interessante notare che in Python2 più di uno è 0stato analizzato come un octinteger(il risultato finale è ancora 0però)

decimalinteger :: = nonzerodigit cifra * | "0"
ottinteger :: = "0" ("o" | "O") octdigit + | "0" octdigit +

1
E qualsiasi idea del perché ci sia "0"+e no"0" ?
lejlot

1
@lejlot, non ancora, ma sono incuriosito. Tuttavia, fa sicuramente parte delle specifiche
John La Rooy

3

Python2 ha usato lo zero iniziale per specificare i numeri ottali:

>>> 010
8

Per evitare questo comportamento (fuorviante?), Python3 richiede prefissi espliciti 0b , 0o, 0x:

>>> 0o10
8

15
La domanda rimane: perché è 00consentito? (E 000, 0000ecc.)
Michael Geary

4
@MichaelGeary: forse perché non può essere ambiguo (00000000 è 0 indipendentemente dalla base) e rimuoverlo spezzerebbe inutilmente il codice? Ancora strano.
RemcoGerlich

5
@RemcoGerlich Se non sbaglio, 01è anche a 1prescindere dalla base.
Holt

2
@ Holt: ma consentendo "0" + "1"? come un caso speciale sarebbe probabilmente ancora più confuso.
RemcoGerlich

4
@RemcoGerlich Non ha mai detto che non l'avrebbe fatto;) Stavo solo dicendo che can't be ambiguousnon è un argomento poiché 01nemmeno può essere ambiguo. IMO, il 00caso è solo un caso speciale perché è quello 0che non dovrebbe essere.
Holt
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.