天天看點

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