【文章标题】: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不是。很奇怪,对吧?