¿Por qué Python 3 permite “00” como literal para 0 pero no permite “01” como literal para 1?

¿Por qué Python 3 permite “00” como literal para 0 pero no permite “01” como literal para 1? ¿Hay alguna buena razón? Esta inconsistencia me desconcierta. (Y estamos hablando de Python 3, que deliberadamente rompió la compatibilidad hacia atrás para lograr objectives como la consistencia).

Por ejemplo:

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

Según https://docs.python.org/3/reference/lexical_analysis.html#integer-literals :

Los literales enteros se describen mediante las siguientes definiciones léxicas:

 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" 

No hay límite para la longitud de los literales enteros aparte de lo que se puede almacenar en la memoria disponible.

Tenga en cuenta que los ceros iniciales en un número decimal distinto de cero no están permitidos. Esto es para desambiguación con literales octales de estilo C, que Python usó antes de la versión 3.0.

Como se señaló aquí, no se permiten los ceros iniciales en un número decimal distinto de cero . "0"+ es legal como un caso muy especial, que no estaba presente en Python 2 :

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

SVN commit r55866 implementó PEP 3127 en el tokenizer, lo que prohíbe los números 0 antiguos. Sin embargo, curiosamente, también añade esta nota:

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

con un indicador especial nonzero que solo lanza un SyntaxError si la siguiente secuencia de dígitos contiene un dígito distinto de cero.

Esto es extraño porque PEP 3127 no permite este caso:

Este PEP propone que la capacidad de especificar un número octal mediante el uso de un cero inicial se eliminará del lenguaje en Python 3.0 (y el modo de vista previa de Python 3.0 de 2.6), y que se levantará un SyntaxError siempre que aparezca un “0” inicial. Inmediatamente seguido por otro dígito .

(énfasis mío)

Por lo tanto, el hecho de que se permitan múltiples ceros está violando técnicamente el PEP, y fue básicamente implementado como un caso especial por Georg Brandl. Hizo el cambio de documentación correspondiente para notar que "0"+ era un caso válido para decimalinteger (anteriormente que había sido cubierto bajo octinteger ).

Probablemente nunca sabremos exactamente por qué Georg eligió hacer "0"+ válido, puede que siempre sea un caso extraño en Python.


ACTUALIZACIÓN [28 de julio de 2015]: esta pregunta condujo a un hilo de discusión animado sobre ideas de python en el que Georg intervino en :

Steven D’Aprano escribió:

¿Por qué se definió de esa manera? […] ¿Por qué escribiríamos 0000 para obtener cero?

Podría decírtelo, pero luego tendría que matarte.

Georg

Más adelante, el hilo generó este informe de error con el objective de deshacerse de este caso especial. Aquí, Georg dice :

No recuerdo la razón de este cambio deliberado (como se ve en el cambio de documentos).

No puedo encontrar una buena razón para este cambio ahora […]

y así lo tenemos: la razón precisa detrás de esta inconsistencia se pierde en el tiempo.

Finalmente, tenga en cuenta que el informe de error fue rechazado: los ceros iniciales continuarán aceptándose solo en los enteros cero para el rest de Python 3.x.

Es un caso especial ( "0"+ )

2.4.4. Literales enteros

 Los literales enteros se describen mediante las siguientes definiciones léxicas:

 integer :: = decimalinteger |  octinteger |  hexinteger |  bininteger
 decimalinteger :: = dígito nonzerodigit * |  "0" +
 nonzerodigit :: = "1" ... "9"
 dígito :: = "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"

Si nos fijamos en la gramática, es fácil ver que 0 necesita un caso especial. Sin embargo, no estoy seguro de por qué el ‘ + ‘ se considera necesario allí. Es hora de cavar a través de la lista de correo dev …


Es interesante notar que en Python2, se analizó más de un 0 como octinteger ( octinteger el resultado final aún es 0 )

 decimalinteger :: = dígito nonzerodigit * |  "0"
 octinteger :: = "0" ("o" | "O") octdigit + |  "0" octdigit +

Python2 usó el cero inicial para especificar números octales:

 >>> 010 8 

Para evitar este comportamiento (¿engañoso?), Python3 requiere prefijos explícitos 0b , 0o , 0x :

 >>> 0o10 8