Por que o Python 3 permite "00" como literal para 0, mas não permite "01" como literal para 1?

111

Por que o Python 3 permite "00" como literal para 0, mas não permite "01" como literal para 1? Existe uma boa razão? Essa inconsistência me deixa perplexo. (E estamos falando sobre Python 3, que quebrou a compatibilidade com versões anteriores de propósito para atingir objetivos como consistência.)

Por exemplo:

>>> from datetime import time
>>> time(16, 00)
datetime.time(16, 0)
>>> time(16, 01)
  File "<stdin>", line 1
    time(16, 01)
              ^
SyntaxError: invalid token
>>>
morsa
fonte
42
Não pode ser removido agora, ou quebrará a compatibilidade com esta questão!
John La Rooy

Respostas:

103

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

Literais inteiros são descritos pelas seguintes definições lexicais:

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"

Não há limite para o comprimento de literais inteiros além do que pode ser armazenado na memória disponível.

Observe que zeros à esquerda em um número decimal diferente de zero não são permitidos. Isso é para desambiguação com literais octais de estilo C, que Python usava antes da versão 3.0.

Conforme observado aqui, zeros à esquerda em um número decimal diferente de zero não são permitidos. "0"+é legal como um caso muito especial, que não estava presente no Python 2 :

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

O commit do SVN r55866 implementou o PEP 3127 no tokenizer, o que proíbe os 0<octal>números antigos . No entanto, curiosamente, também adiciona esta nota:

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

com um nonzerosinalizador especial que só lança um SyntaxErrorse a seguinte sequência de dígitos contiver um dígito diferente de zero.

Isso é estranho porque o PEP 3127 não permite este caso:

Este PEP propõe que a capacidade de especificar um número octal usando um zero à esquerda será removida da linguagem no Python 3.0 (e do modo de visualização do Python 3.0 de 2.6), e que um SyntaxError será gerado sempre que um "0" à esquerda for imediatamente seguido por outro dígito .

(ênfase minha)

Portanto, o fato de que vários zeros são permitidos está tecnicamente violando o PEP e foi basicamente implementado como um caso especial por Georg Brandl. Ele fez a alteração da documentação correspondente para observar que "0"+era um caso válido decimalinteger(anteriormente coberto por octinteger).

Provavelmente nunca saberemos exatamente por que Georg escolheu tornar "0"+válido - pode permanecer para sempre um caso estranho em Python.


ATUALIZAÇÃO [28 de julho de 2015]: Esta questão levou a um animado tópico de discussão sobre ideias de python em que Georg concordou :

Steven D'Aprano escreveu:

Por que foi definido dessa forma? [...] Por que escreveríamos 0000 para obter zero?

Eu poderia te dizer, mas então eu teria que te matar.

Georg

Mais tarde, o tópico gerou este relatório de bug com o objetivo de se livrar deste caso especial. Aqui, Georg diz :

Não me lembro do motivo dessa mudança deliberada (como visto na mudança de documentos).

Não consigo pensar em um bom motivo para essa mudança agora [...]

e assim o temos: a razão precisa por trás dessa inconsistência se perdeu no tempo.

Finalmente, observe que o relatório de bug foi rejeitado: zeros à esquerda continuarão a ser aceitos apenas em inteiros zero para o resto do Python 3.x.

Nneonneo
fonte
6
Por que você diz "Provavelmente nunca saberemos exatamente por que o Georg escolheu ..."? Se alguém que o conhece vir este tópico e o informar sobre isso, então ele pode vir dar sua resposta! (a menos que você saiba que ele está sempre se recusando a discutir seu trabalho anterior em Python, ou alguma circunstância semelhante)
morsa
1
Não entendo por que eles não criaram o segundo octintegercaso do Python 2 "0" octdigit*. 0é um literal octal em C / C ++.
Random832
1
Na verdade, o inglês é um pouco ambíguo a esse respeito. A palavra "outro" pode significar "mais um" ou pode significar "um diferente". Uma interpretação válida em inglês da citação em negrito do PEP 3127 é significar "um SyntaxError será gerado sempre que um '0' inicial for imediatamente seguido por um dígito diferente de '0'" Não tenho certeza se foi isso que realmente se pretendeu ( embora essa interpretação pareça ser apoiada pelo código real), mas em qualquer caso, não acho que seja correto dizer que o PEP é tecnicamente violado sem um esclarecimento adicional dessa frase.
GrandOpener
2
@GrandOpener: Observe que 001é ilegal, enquanto sua interpretação tornaria isso legal (já que o significado de "imediatamente" deve ser bastante inequívoco).
nneonneo
Bom ponto. Portanto, o PEP é definitivamente violado; o que é ambíguo é a natureza exata em que é violado. :)
GrandOpener
17

É um caso especial ( "0"+)

2.4.4. Literais inteiros

Literais inteiros são descritos pelas seguintes definições lexicais:

integer :: = decimalinteger | octinteger | hexinteger | bininteger
decimalinteger :: = dígito não zerodigital * | "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 :: = dígito | "a" ... "f" | "A" ... "F"
bindigit :: = "0" | "1"

Se você olhar para a gramática, é fácil perceber que 0precisa de um caso especial. Não tenho certeza porque o ' +' é considerado necessário lá. É hora de vasculhar a lista de discussão dev ...


É interessante notar que no Python2, mais de um 0foi analisado como um octinteger(o resultado final ainda é 0)

decimalinteger :: = dígito não zerodigital * | "0"
octinteger :: = "0" ("o" | "O") octdigit + | "0" octdigito +
John La Rooy
fonte
1
E alguma ideia do porque existe "0"+e não existe "0"?
lejlot
1
@lejlot, ainda não - mas estou intrigado. Definitivamente faz parte das especificações
John La Rooy
3

Python2 usava o zero à esquerda para especificar os números octais:

>>> 010
8

Para evitar esse comportamento (? Enganosa), Python3 requer prefixos explícitas 0b, 0o, 0x:

>>> 0o10
8
dlask
fonte
15
A questão permanece: por que é 00permitido? (E 000, 0000etc.)
Michael Geary
4
@MichaelGeary: possivelmente porque não pode ser ambíguo (00000000 é 0 independentemente da base) e removê-lo quebraria o código desnecessariamente? Ainda estranho.
RemcoGerlich
5
@RemcoGerlich Se não me engano, 01também é 1independente da base.
Holt
2
@Holt: mas permitindo "0" + "1"? como um caso especial, provavelmente seria ainda mais confuso.
RemcoGerlich
4
@RemcoGerlich Nunca disse que não;) Eu estava apenas dizendo que o can't be ambiguousnão é um argumento, pois 01também não pode ser ambíguo. IMO, o 00caso é apenas um caso especial porque é o 0que não deveria ser.
Holt