115

为什么 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
>>>
4

3 回答 3

106

根据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"

除了可以存储在可用内存中的内容之外,整数文字的长度没有限制。

请注意,不允许在非零十进制数中使用前导零。这是为了消除 Python 在 3.0 版之前使用的 C 风格的八进制文字的歧义。

如此处所述,不允许在非零十进制数中使用前导零。"0"+作为一个非常特殊的情况是合法的,这在 Python 2 中不存在

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

SVN 提交 r55866在标记器中实现了 PEP 3127,它禁止旧0<octal>数字。然而,奇怪的是,它还添加了这个注释:

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

带有一个特殊标志,仅当以下数字序列包含非零数字nonzero时才抛出 a 。SyntaxError

这很奇怪,因为PEP 3127不允许这种情况:

此 PEP 建议将在 Python 3.0(以及 2.6 的 Python 3.0 预览模式)中删除使用前导零指定八进制数的能力,并且每当出现前导“0”时都会引发 SyntaxError紧接着是另一个数字

(强调我的)

因此,允许多个零的事实在技术上违反了 PEP,并且基本上是由 Georg Brandl 作为特例实现的。他对相应的文档进行了更改,以指出这"0"+是一个有效的案例decimalinteger(之前已涵盖在 中octinteger)。

我们可能永远不会确切地知道 Georg 为什么选择 make "0"+valid - 它可能永远是 Python 中一个奇怪的角落案例。


更新[2015 年 7 月 28 日]:这个问题引发了一个关于 python-ideas的热烈讨论线程, Georg 在其中插话

史蒂文·达普拉诺写道:

为什么这样定义?[...] 为什么我们要写 0000 来得到零?

我可以告诉你,但我必须杀了你。

乔治

后来,该线程产生了这个错误报告,旨在摆脱这种特殊情况。在这里,乔治说

我不记得这种故意更改的原因(从文档更改中可以看出)。

我现在无法为这种变化想出一个很好的理由 [...]

因此我们得到了它:这种不一致背后的确切原因已经被时间遗忘了。

最后,请注意错误报告被拒绝:对于 Python 3.x 的其余部分,将继续仅在零整数上接受前导零。

于 2015-07-16T08:01:29.323 回答
17

这是一个特例 ( "0"+)

2.4.4。整数文字

整数文字由以下词法定义描述:

整数 ::= 十进制整数 | 八进制 | 六角整数 | 二进制整数
十进制整数 ::= 非零位数字* | "0"+
非零数字 ::= "1"..."9"
数字 ::= "0"..."9"
八进制数 ::= "0" ("o" | "O") 八进制数+
hexinteger ::= "0" ("x" | "X") hexdigit+
bininteger ::= "0" ("b" | "B") bindigit+
八位数字 ::= "0"..."7"
十六进制数字 ::= 数字 | "a"..."f" | “A”……“F”
二进制数字 ::= "0" | “1”

如果您查看语法,很容易看出0需要特殊情况。我不确定为什么 ' +' 在那里被认为是必要的。是时候挖掘开发邮件列表了……


有趣的是,在 Python2 中,不止一个0被解析为一个octinteger(最终结果仍然0是)

十进制整数 ::= 非零位数字* | “0”
八进制数 ::= "0" ("o" | "O") 八进制数+ | “0”八位数字+
于 2015-07-16T07:52:06.073 回答
2

Python2 使用前导零来指定八进制数:

>>> 010
8

为了避免这种(误导?)行为,Python3 需要明确的前缀0b, 0o, 0x:

>>> 0o10
8
于 2015-07-16T07:26:56.700 回答