奇怪!Python 6 个预声明“常量”表现各异,设计理由是啥?

奇怪!Python 6 个预声明“常量”表现各异,设计理由是啥? Python 预声明常量情况怪异2026 年 8 月 1 日消息Python 有 6 个预声明的“常量”True、False、None、__debug__、Ellipsis也可写作 ...和 NotImplemented。不过它们的表现因某些原因而略有不同。“True”、“False” 和 “None”特殊关键字True、False 和 None 是关键字并非标识符而是独立的词法标记。这在 Python 里很奇怪因为通常变量是在常规名称解析过程中确定而非在词法分析器中。这样产生了有趣的副作用像 x.True 这样的表达式会引发 SyntaxError。那么做出这个决定的理由是什么如果有的话呢关于这些常量还有更多有趣之处不过这与其他常量有关联稍后再讲。“__debug__”特殊布尔常量__debug__ 是布尔常量通常为 True但使用 -O 选项运行时为 False这和在非调试版本中禁用 assert 的思路类似。若在“优化”版本中检查成本过高可用 if __debug__ 包裹代码。不过__debug__ 很特别。它虽是普通标识符和 True、False 及 None 不同却是 Python 语言中唯一不能赋值的标识符。代码示例如下 __debug__ 67File , line 1SyntaxError: cannot assign to __debug__甚至不能将其作为属性赋值 x.__debug__ 67File , line 1SyntaxError: cannot assign to __debug__同样没有其他标识符会这样这是特殊情况。但由于它不是关键字表现与 True、False 和 None 略有不同x.__debug__ 会引发 AttributeError而非 SyntaxError因为它语法上有效只是在查找不存在的属性。有趣的是尝试删除 __debug__ 时有特殊错误信息尽管非特殊情况也会引发 NameError但不适用于删除名为 __debug__ 的属性 del __debug__File , line 1SyntaxError: cannot delete __debug__ del x.__debug__Traceback (most recent call last):File , line 1, inNameError: name x is not defined若 x 已定义会引发 AttributeError。出于某些原因这两种情况都不会引发 SyntaxError和赋值情况不同。题外话“SyntaxError” 是假象说到错误给 __debug__ 赋值是少数几种情况之一代码实际并非语法错误却会引发 SyntaxError。可自行验证 assert (__debug__ : 67)在调试版本中运行此断言会引发 SyntaxError但用 -O 选项时断言不会编译也就不会引发异常。另外两个类似情况是在函数外部使用 yield 或 await assert (yield) assert (await 67)“Ellipsis” 和 “NotImplemented”非真正常量Ellipsis 和 NotImplemented 在参考文档的“常量”部分有记载但和其他 4 个常量不同它们不是“真正的”常量只是普通内置对象因此可被全局变量覆盖 NotImplemented 67 NotImplemented67同样不禁好奇这样设计的理由是什么为何它们不特殊而其他常量却特殊呢覆盖常量情况各不同有趣的是尽管 True、False 和 None 是词法标记但它们也作为普通内置对象存在 import builtins getattr(builtins, True)True getattr(builtins, False)False getattr(builtins, None) is NoneTrue若不使用 getattr就无法直接访问这些内置对象。但更有趣的是setattr 也能起作用 setattr(builtins, True, 67) getattr(builtins, True)67然而当使用词法标记访问时其值并不会改变 TrueTrue__debug__ 也有相同表现 setattr(builtins, __debug__, 67) builtins.__debug__67 __debug__True所以__debug__ 某种程度上可以被赋值但尽管它不是词法标记却和 True、False 和 None 一样是特殊情况对内置模块的更改不会影响它的值因此它确实是一个常量Ellipsis 和 NotImplemented 再次证明它们实际上不是常量 setattr(builtins, Ellipsis, 67) Ellipsis67不过这不会改变 ... 的值 ...Ellipsis所以从某种意义上说... 是真正的常量而 Ellipsis 不是是不是很奇怪呢