【文章标题】:Python’s pre-declared constants are kinda weird 【文章标题】:Python的预声明常量有点奇怪

【文章正文】: 【文章正文】: python has 6 pre-declared “constants”: True, False, None, debug, Ellipsis (or equivalently …), and NotImplemented. but they all behave slightly differently, for some reason. Python有6个预声明的“常量”:True、False、None、debug、Ellipsis(或等价的…)以及NotImplemented。但不知为何,它们的行为都略有不同。

True, False, and None True、False和None True, False, and None are keywords. they aren’t identifiers, they’re just straight up their own lexical tokens. which is really weird; nothing else is like this in python. usually stuff is resolved during regular name resolution, not in the lexer itself. True、False和None是关键字。它们不是标识符,而是直接作为独立的词法单元。这真的很奇怪;Python中没有其他东西是这样的。通常,名称是在常规的名称解析阶段进行解析的,而不是在词法分析器本身中。

an interesting side effect of this is that expressions like x.True raise a SyntaxError. i’m curious as to what the rationale was for this decision (if there was one). 这带来了一个有趣的副作用,即像x.True这样的表达式会引发SyntaxError。我很好奇做出这个决定的理由是什么(如果有的话)。

there’s some more interesting stuff with these constants, but i’ll get to it later, since it ties in with the other constants. 关于这些常量还有一些更有趣的东西,但我稍后再讲,因为它们与其他常量有关联。 debug debug debug is a boolean constant: it’s normally True, but when running with -O, it’s False. the idea is similar to how assert is disabled in non-debug builds: you can wrap code in if debug if the check would be too expensive in an “optimized” build, or something. __debug__是一个布尔常量:它通常为True,但在使用-O选项运行时会变为False。其理念类似于在非调试构建中禁用assert:如果某些检查在“优化”构建中开销太大,你可以用if __debug__将代码包裹起来,诸如此类。

debug is really interesting though, because although it’s a normal identifier (unlike True, False, and None), it’s the only identifier in the language which can’t be assigned to: 不过,__debug__真的很有趣,因为尽管它是一个普通标识符(不像True、False和None),它却是该语言中唯一不能被赋值的标识符:

debug = 67 File "", line 1 SyntaxError: cannot assign to debug you can’t even assign to it as an attribute: debug = 67 File "", line 1 SyntaxError: cannot assign to debug 你甚至不能将其作为属性进行赋值: x.debug = 67 File "", line 1 SyntaxError: cannot assign to debug again, no other identifier behaves like this. this is a true special case. but because it’s not a keyword, it behaves slightly differently to True, False, and None: x.debug = 67 File "", line 1 SyntaxError: cannot assign to debug 同样,没有其他标识符会这样。这是一个真正的特例。 但因为它不是关键字,所以它的行为与True、False和None略有不同: x.debug raises AttributeError (rather than SyntaxError), since it’s syntactically valid; it’s just looking up an attribute which doesn’t exist. x.__debug__会引发AttributeError(而不是SyntaxError),因为它在语法上是合法的;它只是在查找一个不存在的属性。

interestingly, there’s also a special error message for attempting to delete debug (despite the fact that this would raise a NameError anyway if not for the special case), but this doesn’t apply for deleting an attribute named debug: 有趣的是,尝试删除__debug__时也有一个特殊的错误提示(尽管如果没有这个特例,它无论如何都会引发NameError),但这不适用于删除名为__debug__的属性:

del debug File "", line 1 SyntaxError: cannot delete debug >>> del x.debug Traceback (most recent call last): File ” ”, line 1, in NameError: name ‘x’ is not defined if x were defined, an AttributeError would be raised instead. in either case, it’s not a SyntaxError (unlike assignment), for some reason. del debug File "", line 1 SyntaxError: cannot delete debug >>> del x.debug Traceback (most recent call last): File ” ”, line 1, in NameError: name ‘x’ is not defined 如果x已定义,则会引发AttributeError。无论哪种情况,不知为何它都不是SyntaxError(与赋值不同)。

tangent: SyntaxError is a lie 题外话:SyntaxError在撒谎 speaking of errors: assigning to debug is one of only a few cases i’m aware of where a SyntaxError is raised despite something not actually being invalid syntax. here, you can confirm it yourself: 说到错误:据我所知,对__debug__赋值是少数几种在语法实际上并非无效的情况下却引发SyntaxError的情况之一。在这里,你可以自己验证一下:

assert (debug := 67) running that assert in a debug build raises a SyntaxError, but with -O, the assertion is never compiled, and so no exception is raised. assert (debug := 67) 在调试构建中运行该assert会引发SyntaxError,但在使用-O选项时,该断言根本不会被编译,因此不会引发任何异常。

two other instances of this are using yield or await outside of a function: 另外两种类似的情况是在函数外部使用yield或await:

assert (yield) assert (await 67) assert (yield) assert (await 67) Ellipsis and NotImplemented Ellipsis和NotImplemented Ellipsis and NotImplemented are documented in the “constants” section of the reference, but unlike the other 4 constants, they aren’t “real” constants. they’re just normal builtins, so they can be shadowed by globals: Ellipsis和NotImplemented在参考文档的“常量”部分有记载,但与其他4个常量不同,它们不是“真正”的常量。它们只是普通的内置对象,因此可以被全局变量遮蔽:

NotImplemented = 67 NotImplemented 67 again, i’m curious about the rationale here. why is it that these aren’t special, but the other constants are? NotImplemented = 67 NotImplemented 67 同样,我很好奇这里的理由是什么。为什么它们不特殊,而其他常量却特殊呢? overwriting constants 覆写常量 here’s something interesting: despite being lexical tokens, True, False, and None also exist as normal builtins: 这里有个有趣的现象:尽管True、False和None是词法单元,但它们同时也作为普通的内置对象存在:

import builtins getattr(builtins, ‘True’) True getattr(builtins, ‘False’) False getattr(builtins, ‘None’) is None True there’s no way to directly access these without using getattr. import builtins getattr(builtins, ‘True’) True getattr(builtins, ‘False’) False getattr(builtins, ‘None’) is None True 如果不使用getattr,就没有办法直接访问它们。

but here’s where things get really interesting: setattr also works! 但真正有趣的地方在于:setattr也起作用!

setattr(builtins, ‘True’, 67) getattr(builtins, ‘True’) 67 however, this doesn’t change the value when accessed with the lexical token: True True but debug has the same behavior! setattr(builtins, ‘True’, 67) getattr(builtins, ‘True’) 67 然而,这并不会改变通过词法单元访问时的值: True True 但__debug__也有同样的行为!

setattr(builtins, ‘debug’, 67) builtins.debug 67 debug True so debug can sorta be assigned to, but despite not being a lexical token, it’s special cased just like True, False, and None: its value is unaffected by changes to the builtins module. so it really is a constant! setattr(builtins, ‘debug’, 67) builtins.debug 67 debug True 所以__debug__在某种程度上是可以被赋值的,但尽管它不是词法单元,它却像True、False和None一样被特殊处理:它的值不受builtins模块更改的影响。所以它确实是一个常量!

Ellipsis and NotImplemented are, once again, not actually constants: 而Ellipsis和NotImplemented,再一次证明,实际上并不是常量:

setattr(builtins, ‘Ellipsis’, 67) Ellipsis 67 this doesn’t change the value of … though: setattr(builtins, ‘Ellipsis’, 67) Ellipsis 67 不过这并不会改变…的值:

… Ellipsis so in some sense, … is a real constant, but Ellipsis isn’t. weird, right? … Ellipsis 所以在某种意义上,…是一个真正的常量,而Ellipsis不是。很奇怪,对吧?