Почему Python 3 разрешает "00" как литерал для 0, но не позволяет "01" как литерал для 1?

Почему Python 3 разрешает "00" как литерал для 0, но не позволяет "01" как литерал для 1? Есть ли веская причина? Эта непоследовательность меня озадачивает. (И мы говорим о Python 3, который намеренно нарушил обратную совместимость, чтобы достичь целей, таких как согласованность.)

Например:

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

Ответ 1

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

Целочисленные литералы описываются следующими лексическими определениями:

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"

Нет предела для длины целочисленных литералов, кроме того, что могут храниться в доступной памяти.

Обратите внимание, что ведущие нули в ненулевом десятичном числе недопустимы. Это для значения с восьмеричными буквами C-стиля, которые Python используется до версии 3.0.

Как отмечено здесь, ведущие нули в ненулевом десятичном числе недопустимы. "0"+ является законным как особый случай, который отсутствовал в Python 2:

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

SVN commit r55866 реализовал PEP 3127 в токенизаторе, который запрещает старые 0<octal> номера. Однако, как это ни странно, это также добавляет эту заметку:

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

со специальным флагом nonzero, который генерирует только SyntaxError, если следующая последовательность цифр содержит ненулевую цифру.

Это нечетно, потому что PEP 3127 не разрешает этот случай:

Этот PEP предлагает, чтобы возможность указывать восьмеричное число с использованием начального нуля была удалена из языка в Python 3.0 (и в режиме предварительного просмотра Python 3.0), и что Синтаксический эффект будет возникать всякий раз перед "0" сразу следует другая цифра.

(акцент мой)

Таким образом, тот факт, что допускаются множественные нули, технически нарушает PEP, и в основном был реализован в качестве особого случая Георга Брандла. Он сделал соответствующее изменение документации, отметив, что "0"+ был действительным случаем для decimalinteger (ранее это было описано в octinteger).

Мы, вероятно, никогда точно не узнаем, почему Георг решил сделать "0"+ действительным - он может навсегда оставаться нечетным углом в Python.


ОБНОВЛЕНИЕ [28 июля 2015 г.]: этот вопрос привел к оживленной дискуссионной теме о идеях python, в которой Георг подшутил:

Стивен Д'Арпано писал (а):

Почему это было определено именно так? [...] Зачем нам писать 0000, чтобы получить нуль?

Я мог бы рассказать вам, но тогда я должен был бы убить вас.

Георг

Впоследствии поток породил этот отчет об ошибках, чтобы избавиться от этого особого случая. Здесь Георг говорит:

Я не помню причины этого преднамеренного изменения (как видно из изменения документов).

Я не могу придумать вескую причину для этого изменения сейчас [...]

и, следовательно, мы имеем это: точная причина этой несогласованности теряется во времени.

Наконец, обратите внимание, что отчет об ошибке был отклонен: ведущие нули будут по-прежнему приниматься только на нулевые целые числа для остальной части Python 3.x.

Ответ 2

Это частный случай ("0"+)

2.4.4. Целочисленные литералы

Integer literals are described by the following lexical definitions:

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"

Если вы посмотрите на грамматику, легко увидеть, что 0 нужен специальный случай. Я не уверен, почему "+" считается там необходимым. Время прорыть список рассылки dev...


Интересно отметить, что в Python2 более одного 0 анализировалось как octinteger (конечный результат все еще 0)

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

Ответ 3

Python2 использовал начальный ноль, чтобы указать восьмеричные числа:

>>> 010
8

Чтобы избежать этого (вводящего в заблуждение?) поведения, Python3 требует явных префиксов 0b, 0o, 0x:

>>> 0o10
8