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

111

Почему 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
>>>
морж
источник
42
Его нельзя удалить сейчас, иначе это нарушит обратную совместимость с этим вопросом!
Джон Ла Рой

Ответы:

103

Согласно 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флагом, который выдает a, только SyntaxErrorесли следующая последовательность цифр содержит ненулевую цифру.

Это странно, потому что PEP 3127 не допускает этого случая:

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

(курсив мой)

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

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


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

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

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

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

Georg

Позже поток породил этот отчет об ошибке с целью избавиться от этого особого случая. Здесь Георг говорит :

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

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

и, таким образом, мы имеем: точная причина этого несоответствия потеряна со временем.

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

nneonneo
источник
6
Почему вы говорите: «Мы, вероятно, никогда не узнаем точно, почему Георг решил ...»? Если кто-то, кто знает его, увидит эту ветку и сообщит ему об этом, он может прийти и дать свой ответ! (если вы не знаете, что он навсегда откажется обсуждать свои прошлые работы по Python или какие-то подобные обстоятельства)
морж
1
Я не понимаю, почему они просто не сделали второй octintegerкорпус Python 2 "0" octdigit*. 0является восьмеричным литералом в C / C ++.
Random832
1
На самом деле английский в этом отношении несколько неоднозначен. Слово «другой» может означать «еще один» или «другой». Одна допустимая английская интерпретация выделенной жирным шрифтом цитаты из PEP 3127 - это означать, что «SyntaxError будет возникать всякий раз, когда за ведущим '0' сразу следует цифра, отличная от '0'». Я не уверен, что это было на самом деле предназначено ( хотя эта интерпретация, по-видимому, поддерживается фактическим кодом), но в любом случае я не думаю, что правильно сказать, что PEP технически нарушен, без дополнительных разъяснений этого предложения.
GrandOpener
2
@GrandOpener: Обратите внимание, что 001это незаконно, тогда как ваша интерпретация сделает это законным (поскольку значение слова «немедленно» должно быть совершенно однозначным).
nneonneo
Хорошая точка зрения. Так что PEP определенно нарушается; что неясно, так это точный характер его нарушения. :)
GrandOpener
17

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

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

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

целое число :: = десятичное число | октинтегр | шестнадцатеричный | бинтегер
decimalinteger :: = ненулевая цифра * | «0» +
nonzerodigit :: = "1" ... "9"
цифра :: = "0" ... "9"
octinteger :: = "0" ("o" | "O") octinteger +
hexinteger :: = "0" ("x" | "X") hexdigit +
bininteger :: = "0" ("b" | "B") bindigit +
octdigit :: = "0" ... "7"
шестнадцатеричный :: = цифра | "а" ... "е" | «А» ... «Ф»
bindigit :: = "0" | "1"

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


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

decimalinteger :: = ненулевая цифра * | "0"
octinteger :: = "0" ("o" | "O") octinteger + | «0» восьмицифровая +
Джон Ла Рой
источник
1
А почему есть, "0"+а почему нет "0"?
lejlot
1
@lejlot, пока нет - но я заинтригован. Хотя это определенно часть спецификации
Джон Ла Рой
3

Python2 использовал начальный ноль для указания восьмеричных чисел:

>>> 010
8

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

>>> 0o10
8
dlask
источник
15
Остается вопрос: почему 00разрешено? (И 000, 0000и т.д.)
Майкл Гири
4
@MichaelGeary: возможно, потому, что это не может быть двусмысленным (00000000 равно 0 независимо от основания), и его удаление без необходимости нарушит код? Все еще странно.
RemcoGerlich
5
@RemcoGerlich Если я не ошибаюсь, 01тоже 1независимо от базы.
Холт
2
@Holt: но разрешить "0" + "1"? как частный случай, вероятно, еще больше запутает.
RemcoGerlich
4
@RemcoGerlich Никогда не говорил, что не будет;) Я просто сказал, что can't be ambiguousэто не аргумент, поскольку 01тоже не может быть двусмысленным. ИМО, 00случай - это просто особый случай, потому 0что так быть не должно.
Холт