Python 预先声明的常量有点奇怪
Python 对 `True`、`False`、`None`、`Ellipsis` 和 `__debug__` 等内置常量的处理暴露出一些令人意外的边角情况,例如条件代码在编译时被剥离,或早期版本里布尔值可以被重新赋值等历史怪癖。评论者借这些例子讨论 Python 更广泛的设计取舍:它累积下来的“包袱”、类型系统和 async 系统中那些怪异角落,以及臭名昭著的打包混乱;与此同时,Python 也凭借易用、内置丰富、适合脚本、数据处理和胶水代码等优点而广受欢迎。讨论还将 Python 与 PHP、JavaScript、Ruby 和 Haskell 等语言进行对比,突出理论上的语言优雅、向后兼容性与大规模实践可用性之间反复出现的张力。
特殊常量与条件编译
- 讨论主要围绕 Python 的“预先声明的常量”:
True、False、None(关键字)与Ellipsis、NotImplemented、__debug__(带有特殊处理的标识符)。 __debug__被特别指出很奇怪:在PYTHONOPTIMIZE/-O下,if __debug__:代码块会在编译时被完全剥离,这使它与assert一起成为一种条件编译形式。- 这也是为什么禁止给
__debug__赋值:编译器在消除代码时会把它当作常量。 - 有几位评论者承认自己从未听说过
__debug__,也不知道它与assert的交互,并指出如果把assert(误)用于关键校验,确实可能引发严重安全漏洞。 - 还提到 3.15 中即将加入的
TYPE_CHECKING,它是另一个行为类似Ellipsis/NotImplemented的预先声明常量。
布尔值的历史怪癖与设计“包袱”
- 早期 Python 没有
True/False;用户会把它们定义为1/0。Python 2 允许重新赋值,Python 3 则把它们变成了关键字。 bool仍然是int的子类,这一点至今仍让人意外,也曾造成过 bug。- 更广泛的观点是:把
bool/空值事后改造进一种语言并不容易;C 和 Python 被作为例子提及。 - 还有人讨论更广泛的语言设计遗憾:缺少内置的多维数组、位数组,以及标准化的小型向量类型。
Python 与其他语言及其演进
- 一派观点认为 Python 年代久远、慢、脆弱、类型系统薄弱且包装混乱;他们声称 PHP 社区之类的群体进化得更积极,也吸取了更好的教训(例如强制类型、统一工具链)。
- 另一派则列举了 Python 的大量改进:类型标注、async/await、性能优化、f-string、模式匹配、字典合并运算符、dataclass、GIL 移除工作、
pathlib等。 - 批评者认为这些特性往往只是照搬想法,却没有带来关键语义(非强制类型、非穷尽模式匹配、async 的“函数着色”),把它们称为“反特性”。
- 与 JavaScript、PHP、Ruby、Racket/Scheme/Haskell 的比较很常见;大家对于哪种语言“更怪”、更一致或更适合初学者看法不一。
对初学者的友好度与使用场景
- 有人认为 Python 充满边角案例,不适合作为第一门语言;也有人认为它的 REPL、内置丰富的标准库、可读性以及对 Windows 的支持,使它成为非常优秀的教学语言和“胶水”语言。
- 有些人把显著缩进、真值性和鸭子类型称为“怪”,另一些人则认为它们一致且易于学习。
生态、打包与导入
- 很多人抱怨依赖管理、虚拟环境以及工具链碎片化;
uv被赞为一项重大改进,但尚未成为标准。 - 导入语义(
__name__ == "__main__"、包与文件系统、__init__.py、相对导入)在一些人看来并不直观,但也有人为其辩护,认为这与 Python 的模块模型保持一致。