天天看点

substring不是可以识别的内置函数_25. None、True与False 的故事(解释字面量、关键字与内置变量)...

substring不是可以识别的内置函数_25. None、True与False 的故事(解释字面量、关键字与内置变量)...

本系列文章译自Python之父 Guido van Rossum 的系列博客“The History of Python”。这个博客系列对我们理解Python及其演变很有帮助,经Guido同意,在这里翻译推荐给大家,希望大家喜欢,也请大家多多指教!

系列地址: http:// blog.kantli.com/theme/1

1. 缘起

最近,有人通过邮件向我提了一个有趣的问题:

关键字与字面量的区别何在?为什么 Python 3 中的 True 和 False 是关键字,而不是字面量?我前段时间惊恐地发现,在 Python 2 中,我们可以给 True/False 赋值。

于是我研究了下,发现在 PEP 285 中,True、False 与 None 一样,都是常量(constants)。Python 2.4 之后,禁止用户给 None 赋值,但在 Python 3 之前,对 True/False 的赋值一直是被允许的。

是有什么特殊原因,让 None 一开始就作为变量而不是字面量吗?

2. 关键字 VS 字面量

先来回答第一个问题:关键字与字面量的区别。

在编程句法中,关键字也叫保留字,类似于这门语言中的识别器,或者从解析器的角度看,就像某种 token。

识别器由字母、数字及下划线组成,但不能以数字开头(这是 Python 中的定义,其它语言,如 C和 Java 的定义也类似)。

关于关键字,最重要的一点是,它们不可以用作变量名称(方法名、类名等等)。大家比较熟悉的 Python 关键字包括 if、while、for、and、or 等等。

而字面量是一个常量的值的表示

,常见的字面量包括数字(如 42,3.14,1.6e-10 等)和字符串(如 “Hello, world”)。解析器可以识别字面量,但识别的具体规则一般会很复杂。比如在 Python 3 中,以下都是数字字面量:

123
           

而以下则不是:

. # 点
e10 # 识别器
0y12 # 字面量 0 加一个识别器 y12
0xffe+10 # 字面量 0xffe 加一个加号以及数字 10
           

注意,字面量并不是常量。我们经常在代码中定义常量,如:

MAX_LEVELS = 15
           

这里,15 是字面量,而 MAX_LEVEL 则是一个识别器,由于全部都是大写字母,因此,用户可能不会在代码中改变其值,也就是说,可能是一个常量——不过,常量中使用大写字母只是惯例而已,Python 解析器并不会因为变量名由大写字母组成就把它当做常量,也不强制所有常量都用大写字母表示。

但反过来写就会被解析器拒绝:

15 = MAX_LEVELS
           
因为赋值操作符(=)的左边必须是一个变量,而字面量不可以作为变量名。

(变量的准确定义非常复杂,有些看着像表达式的,其实也是变量,比如 d[k],(a, b),foo.bar 等等——不过 f(),() 或 42 就不是变量。在 del 语句中,也使用同样的变量定义。)

3. 解析方式

接下来,我们看 None,True 和 False。

先来看 None,因为它在语言诞生之初就有了。(相对来说,True 和 False 是后期添加的,最初是在 Python 2.2.1 版本。)

None 是一个单例对象(即语言中只有一个 None),表示值的缺失。

例如,假设 d 是一个字典,如果 d 中有键 k 的话,d.get(k) 会返回 d[k] ,否则就返回 None。

早期版本中,None 只是 Python 的一个“内置名称”,解析器并不知道什么 None,正如它也不知道 int,float,str 等内置类型,或 KeyError 、 ZeroDivisionError 等内置异常。所有这些,对解析器而言都是识别器,在解析用户代码时,查找它们的过程与查找其它名称的过程是一样的(比如用户自己定义的方法或变量)。

在以下代码中,每一行都是同样解析,产生一样的解析树(<name> = <name>):

x = None
x = int
x = foobar
           

而下面的代码则会产生另一种解析树(<name> = <literal>):

x = 42
x = 'hello'
           

因为解析器处理数字、字符串等字面量的方式与处理识别器的方式不同。

结合这个例子与之前 MAX_LEVEL 的例子,我们知道,如果左右互换,前面三行代码是可以被解析器接受的( = ),而后面两行则不行( = 不成立)。

4. 内置名称 VS 关键字

这样设计的结果是:如果你想恶心使用你代码的人,可以给内置变量重新赋值,比如:

int = float
def parse_string(s):
    return int(s)
print(parse_string('42')) # 将打印42.0
           

有些人可能会说:“这有什么大不了的?正常程序员肯定不会这样写。”而另一些人则完全目瞪口呆:“怎么可能有这种允许赋值给内置名称的傻逼语言?!”

这个问题比较微妙,与一致性的保持和语言的发展历史有关。

我相信,如果不查文档,你肯定写不出 Python 中的所有内置名称(至少我做不到),而且我也相信,很多人并不认识每一个内置名称。(想尝试的话,可以用 dir(builtins) 命令)

就以比较奇怪的内置名称 copyright、credits 与 license为例,由于它们的存在,Python 才能在每次打开交互窗口时发出问候语:

Python 3.4.0a4+ (default:0917f6c62c62, Oct 22 2013, 10:55:35)
[GCC 4.2.1 Compatible Apple LLVM 4.2 (clang-425.0.28)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> credits
Thanks to CWI, CNRI, BeOpen.com, Zope Corporation and a cast of thousands
for supporting Python development. See www.python.org for more information.
>>>
           

然而,是不是因为需要发出问候语,就禁止用户使用 credits 作为变量或参数名称呢?我觉得不然。显然,不少用户根本就没有意识到这些隐晦内置名称的存在,禁止他们使用这些词就会变得很奇怪。而这个问题并没有什么明确的界限。比如说,很多人在函数或方法中使用 str 、 len 、compile 或 format 作为参数名称。

另外,假如用户在 Python 2.5 的代码中使用 bytes 作为变量名称,而在 Python 2.6 中,bytes 已经是一个内置函数名(实际上是 str 的化名),那么,原来的代码是不是必须修改呢?恐怕也不合理。(即便在 Python 3 中,bytes 成为一种基本数据类型之后,依然可以作为变量名。)

另一方面,用户显然不能把 if 或 try 作为变量名称,因为它们是保留字(关键字),解析器处理的方式不一样。而且用户不管用不用得上,都必须记住这些保留字,才能避免使用它们。因此,我们得尽可能减少保留字的数量,每次增加关键字,核心开发者们都要斟酌犹豫很久。

事实上,很多新特性的提案,都因为需要引入新的关键字而被拒绝或调整了。当我们确定要添加一个新的关键字时,至少会提前一个版本发出 depreciation 警告。(另外,其实也有一个让开发者提前使用新关键字的方法,比如“from future import with_statement”。)

内置名称没有这个问题,碰巧使用了内置名称的代码依然是可以正常运行的(只要不在同一个函数中调用被重新赋值的内置名称)。添加内置名称时,我们还是比较保守的,但至少不用担心因为添加内置名称导致原有代码无法运行。

这种设计的唯一(轻微)代价就是,有时候有些人会利用这个机制来搞一些恶作剧。而一个人要写出烂代码有一万种方式,我不觉得这种设计是什么严重的问题。

5. None、True、False成为关键字

讨论了这么多内置名称和关键字的区别后,再来看 None:为什么我们最终还是把 None 作为保留字?

坦白说,可能主要出于社会因素考虑。None 不像其它内置名称或异常,它在 Python 中的地位非常重要,事实上,我们不可能在使用 Python 的时候不用 None。因此,当大家发现 None 可以被赋值时,不免会感到惊恐(正如提问者一样)。同时,根据 Python 的名称查找机制,查找 None 的速度是比较慢的,因为至少要查找两个字典(查找内置变量字典前,至少会先查找全局变量字典)。

我们最终认为,把 None 作为关键字也没什么问题(实际代码中,不会有人真的给它赋值),而且可以让一些代码运行得稍微快一点,并避免可能存在的错误代码。这个改动的唯一影响就是,需要修改一下解析器和官方文档——因此,我们也没什么好犹豫的。

True 和 False 的情况有所不同。Python 中一开始并没有这两个内置名称,很多人会在自己的代码中定义对应的常量,比如 true/false,True/False 或 TRUE/FALSE。我不记得哪种方式更受欢迎了,但当我们引入 True 和 False 的时候,显然不想影响大家之前所写的代码。(否则会使很多包无法兼容新版本)

因此,我们其实不得不把 True 和 False 作为内置常量,而不是关键字引入。随着时间的推移,自己定义 True 和 False 的代码越来越少,最终,随着 Python 3 的发布,我们终于有机会对语言做一次清理,就把 True 和 False 也设为关键字了,正如 None 一样。

6. 总结

如大家所见,如果了解当时的情况,这整个发展过程是完全符合逻辑的。:-) 抱歉,对这个问题的回复有点长了,我希望对大家有所启发。

更新:我好像忘记回答 None/True/False 到底是字面量还是关键字了。我的答案是,它们既是字面量也是关键字。解析器是把它们当做关键字的,而在一般代码中,它们是作为常量使用的,因而也是字面量。有些人可能会觉得,类似于 {‘foo’: 42} 的表达式也是字面量,个人认为,可能不应该叫字面量,否则 { ‘foo’: x+1} 是不是字面量呢?在文档中,它们其实都被称为“表达(displays)”。
公众号:ReadingPython