为什么Python 3允许使用"00"表示0,但不允许使用"01"表示1?

118

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
>>>

43
现在不能删除它,否则将破坏与此问题的向后兼容性! - John La Rooy
3个回答

108

根据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<八进制数> 数字。然而,奇怪的是它也添加了这个注释:

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

使用特殊的nonzero标志,仅当以下数字序列包含非零数字时才引发SyntaxError异常。

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

  

该PEP建议从Python 3.0(以及2.6的Python 3.0预览模式)中删除使用前导零指定八进制数的能力,并且只有当前导“0”紧随另一个数字时,才会引发SyntaxError

(强调我的)

因此,允许多个零的事实在技术上违反了PEP,并且基本上是由Georg Brandl作为特例实现的。他进行了相应的文档更改,指出“0”+decimalinteger的有效情况(以前已经被octinteger覆盖)。

我们可能永远不会确切地知道Georg选择使“0”+有效的原因 - 它可能会一直保留在Python的奇怪角落中。


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

  

Steven D'Aprano写道:

  
    

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

  
  

我可以告诉你,但然后我就得杀了你。

  

Georg

后来,该帖子衍生出这个错误报告,旨在摆脱这种特殊情况。在此,Georg说

  

我不记得这个故意更改的原因(从文档更改中看到)。

  

我现在无法想出这种更改的好理由[...]

因此,我们拥有它:这种不一致性背后的确切原因已经逝去。

最后,请注意,该错误报告已被拒绝:在Python 3.x的其余部分中,仅接受零整数的前导零。


6
为什么你说“我们可能永远不会知道乔治为什么选择...”?如果认识他的人看到这个帖子并告诉他,他可能会给出答案!(除非你知道他已经永远拒绝讨论过去的 Python 工作,或者类似的情况) - walrus
1
我不明白为什么他们没有将第二个Python 2的octinteger情况改为"0" octdigit*。在C/C++中,0是一个八进制字面量。 - Random832
1
实际上,在这方面,英语有点模糊。单词“another”可以意味着“再来一个”,也可以意味着“另一个”。PEP 3127中粗体引用的一种有效的英语解释是指“每当前导'0'紧接着一个不为'0'的数字时,将引发SyntaxError”。我不确定这是否是实际意图(尽管该解释似乎得到了实际代码的支持),但无论如何,我认为如果没有对该句子进行额外澄清,就不能准确地说PEP被技术性违反了。 - GrandOpener
2
@GrandOpener:请注意,001是非法的,而您的解释会使其合法(因为“立即”一词的含义应该是非常明确的)。 - nneonneo
好的观点。因此,PEP肯定被违反了;不明确的是它被违反的具体方式。 :) - GrandOpener
@海象:Georg 发言了!不幸的是,他只是说他不记得更改原因,也想不出现在进行更改的好理由。所以我猜我们永远都不会知道他为什么这样做了。 - nneonneo

17

这是一个特殊情况("0"+

2.4.4. 整数字面量

整数字面量由以下词法定义描述:
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 需要一个特殊情况。我不确定为什么 '+' 在那里被认为是必要的。现在是时候翻阅开发邮件列表了...


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

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

1
有任何想法为什么要用 "0"+ 而不是 "0" ? - lejlot
1
@lejlot,目前还没有——但我很感兴趣。这肯定是规格的一部分。 - John La Rooy

2

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

>>> 010
8

为避免这种(误导性?)的行为,Python3要求显式前缀0b0o0x
>>> 0o10
8

16
问题在于:为什么允许使用“00”(以及“000”、“0000”等)? - Michael Geary
6
可能是因为它不会产生歧义(无论进制如何,00000000都是0),而且删除它将毫无必要地破坏代码。但仍然很奇怪。 - RemcoGerlich
6
如果我没理解错的话,“01”在任何进制下都是“1”。 - Holt
2
@Holt:但是允许"0"+"1"作为特殊情况可能会更加令人困惑。 - RemcoGerlich
4
@RemcoGerlich从未说过这不可能;) 我只是在说“不能有歧义”不是一个论点,因为“01”也不能有歧义。在我看来,“00”情况只是一个特例,因为它代表的是数字0,本不应该有歧义。 - Holt
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接