一個(gè) Python 程序是由一個(gè)解析器讀取的。解析器的輸入是一個(gè)由詞法分析器生成的符號(hào)流。本章介紹了詞法分析器如何將文件分解為符號(hào)。
Python 將程序文本以 Unicode 代碼點(diǎn)的方式讀入;源文件的編碼格式由編碼聲明給出,其默認(rèn)值為 UTF-8 ,詳見 PEP 3120 。源文件無法被解析時(shí),會(huì)引發(fā) SyntaxError 異常。
一個(gè) Python 程序會(huì)被分成多個(gè)邏輯行。
邏輯行使用一個(gè) NEWLINE 標(biāo)記結(jié)尾。除非在語法中允許使用 NEWLINE (例如,復(fù)合語句中的聲明語句之間),否則聲明語句不能跨多個(gè)邏輯行。邏輯行是由一個(gè)物理行,或多個(gè)通過顯示或隱式行連接規(guī)則連接在一起的物理行組成。
物理行是一個(gè)以行尾序列結(jié)尾的字符序列。在源文件中,任何平臺(tái)下標(biāo)準(zhǔn)的行終止序列都可以使用-在 Unix 平臺(tái)上使用 ASCII LF(換行)符,在 Windows 平臺(tái)上使用 ASCII 字符序列CR LF(回車加換行),在老版本的 Macintosh 平臺(tái)上使用 ASCII CR(回車)符。無論使用什么平臺(tái),所有這些形式都可以使用。
使用嵌入式Python開發(fā)時(shí),需要使用標(biāo)準(zhǔn) C 語言約定的換行符將源代碼傳遞給 Python API(字符 \n 表示 ASCII LF,即行終止符)。
注釋以(#)字符開始(且此時(shí)該符號(hào)不是當(dāng)前字符串的一部分),以該物理行的結(jié)尾為結(jié)束。如果沒有調(diào)用隱式行連接,那么注釋即意味著邏輯行的結(jié)尾。注釋會(huì)被語法分析所忽視,它們不會(huì)被作為程序代碼處理。
如果第一或第二行的 Python 腳本注釋能夠匹配正則表達(dá)式 coding[=:]\s*([-\w.]+),該注釋會(huì)被當(dāng)做編碼聲明處理;該表達(dá)式的第一部分定義了源代碼文件的編碼格式。此表達(dá)式的推薦形式是
# -*- coding: <encoding-name> -*-
該表達(dá)式也可以被 GNU Emacs 所識(shí)別,Bram Moolenaar’s VIM 識(shí)別的表達(dá)式形式為
# vim:fileencoding=<encoding-name>
如果未發(fā)現(xiàn)編碼聲明,則默認(rèn)編碼格式為 UTF-8。另外,如果此文件的第一個(gè)字節(jié)是 UTF-8 字節(jié)序標(biāo)記(b'\xef\xbb\xbf'),則聲明的編碼為 UTF-8(微軟的 notepad 支持這種編碼格式)。
如果聲明了一種編碼格式,則編碼格式名稱必須可以被 Python 識(shí)別。所有的詞法分析,包括字符串、注釋以及標(biāo)識(shí)符都需要使用這種編碼格式。編碼聲明必須出現(xiàn)在其自己的行中。
兩個(gè)或者多個(gè)物理行可以使用反斜線(\)合并為一個(gè)邏輯行,例如:當(dāng)一個(gè)物理行以反斜線結(jié)束,且該反斜線不是字符串值或注釋的一部分時(shí),它就與下面的物理行合并構(gòu)成一個(gè)邏輯行,并將反斜線及行結(jié)束符刪除。例如:
if 1900 < year < 2100 and 1 <= month <= 12 \
and 1 <= day <= 31 and 0 <= hour < 24 \
and 0 <= minute < 60 and 0 <= second < 60: # Looks like a valid date
return 1
以反斜線結(jié)尾的行不能在其后加注釋,反斜線不能延續(xù)注釋行。除了字符串文本,反斜線也不能延續(xù)語言符號(hào)(也就是說,其他不是字符串文本的語言符號(hào)不可以使用反斜線橫跨多個(gè)物理行)。在字符串文本外的其他地方出現(xiàn)反斜線都是非法的。
圓括號(hào)、 方括號(hào)或大括號(hào)中的表達(dá)式可以跨多個(gè)物理行,而無需使用反斜杠。例如:
month_names = ['Januari', 'Februari', 'Maart', # These are the
'April', 'Mei', 'Juni', # Dutch names
'Juli','Augustus', 'September', # for the months
'Oktober', 'November', 'December'] # of the year
可以在隱式行連接的末尾添加注釋。接續(xù)行的縮進(jìn)可以不考慮。并且允許出現(xiàn)空的接續(xù)行。在隱式的接續(xù)行中是不存在 NEWLINE 符號(hào)的。隱式的行連接在三重引用串(后述)中也是合法的,在那種情況下不能添加注釋。
一個(gè)只包含空格符、制表符、換頁符和可能是注釋的邏輯行可以忽略(也就是說沒有 NEWLINE 符號(hào)產(chǎn)生)。在交互式輸入語句時(shí),空行的處理可能不同,其依賴于輸入-計(jì)算-輸出循環(huán)的實(shí)現(xiàn)方式。在標(biāo)準(zhǔn)的交互實(shí)現(xiàn)中,一個(gè)純粹的空邏輯行(即不包含任何東西,甚至是空白和注釋)可以結(jié)束多行語句。
位于邏輯行開始前的空白(空格和制表符)用于計(jì)算行的縮進(jìn)層次,該層次可用于語句的分組。
制表符被(從左到右)替換為一至八個(gè)空格,這樣直到包括替換部分的總字符數(shù)是八的倍數(shù)(這與 Unix 中使用的規(guī)則是相同的)。第一個(gè)非空字符前的空格總數(shù)決定了行的縮進(jìn)。不能使用反斜線在多個(gè)物理行之間對(duì)縮進(jìn)進(jìn)行拆分;第一個(gè)反斜線之前的空白字符用于檢測(cè)縮進(jìn)。
在以下情形下縮進(jìn)會(huì)被認(rèn)為是不符合邏輯的:一個(gè)源文件中既包含制表符又包含空格,此時(shí)如果該文件的意義依賴于包含在空格符中的制表符時(shí),就會(huì)觸發(fā) TabError 異常。
跨平臺(tái)兼容性注意:由于非 UNIX 平臺(tái)上文本編輯器的特性,在單個(gè)源文件中混合使用空格符和制表符的縮進(jìn)是不明智的。還需要注意的是,不同的平臺(tái)可能會(huì)顯式的限制最大縮進(jìn)級(jí)別。
雖然在行首可能會(huì)出現(xiàn)換頁符;但它在以上的縮進(jìn)計(jì)算中會(huì)被忽略。出現(xiàn)在其他位置的換頁符的作用是不確定的(比如,它可能將空格數(shù)重置為0)。
連續(xù)行的縮進(jìn)層次用于生成 INDENT 及 DEDENT 語言符號(hào),在此過程中使用了堆棧,如下所述。
文件的第一行未被讀取之前,一個(gè)0被壓入棧中;它以后也不會(huì)被彈出來。被壓入棧中的數(shù)字都從棧底向棧頂增長,在每個(gè)邏輯行的開頭處,將行的縮進(jìn)層次與棧頂元素比較,如果相等則什么都不做。如果大于棧頂元素,則將其壓入棧中并生成一個(gè) INDENT 語言符號(hào)。如果小于棧頂元素,那么其應(yīng)該是堆棧中已經(jīng)存在的數(shù)字中的一個(gè),堆棧中所有大于它的數(shù)都將被彈出,并且每個(gè)彈出的數(shù)字都會(huì)生成一個(gè) DEDENT 語言符號(hào)。在文件的結(jié)尾,每個(gè)仍留在棧中且大于0的元素都會(huì)生成一個(gè) DEDENT 語言符號(hào)。
這里有一個(gè)正確(盡管有點(diǎn)亂)縮進(jìn)格式的 Python 代碼的例子:
def perm(l):
# Compute the list of all permutations of l
if len(l) <= 1:
return [l]
r = []
for i in range(len(l)):
s = l[:i] + l[i+1:]
p = perm(s)
for x in p:
r.append(l[i:i+1] + x)
return r
下面的例子展示了多種縮進(jìn)錯(cuò)誤:
def perm(l): # error: first line indented
for i in range(len(l)): # error: not indented
s = l[:i] + l[i+1:]
p = perm(l[:i] + l[i+1:]) # error: unexpected indent
for x in p:
r.append(l[i:i+1] + x)
return r # error: inconsistent dedent
(實(shí)際上,前三種錯(cuò)誤都是由解析器檢測(cè)出來的,只有最后一個(gè)錯(cuò)誤是由詞法分析器發(fā)現(xiàn)的——return r的縮進(jìn)層次與堆棧中彈出的數(shù)字不匹配。)
除非位于邏輯行的行首或者字符串當(dāng)中,空格符、制表符以及換頁符都可以用于分割語言符號(hào)。只有當(dāng)兩個(gè)符號(hào)串接在一起可能會(huì)被解釋為不同的符號(hào)時(shí),才會(huì)在兩個(gè)符號(hào)之間增加空格(例如: ab 是一個(gè)符號(hào),但 a b 卻是兩個(gè)符號(hào))。
除了 NEWLINE, INDENT 和 DEDENT,還有以下幾類語言符號(hào):標(biāo)識(shí)符、關(guān)鍵字、文本、運(yùn)算符及分隔符。空格符不是語言符號(hào)(除了斷行符,之前討論過),但是可以用來分隔語言符號(hào)。當(dāng)解釋某個(gè)語言符號(hào)存在歧義時(shí),該語言符號(hào)被看作是由一個(gè)盡可能長的字符串組成的合法符號(hào)(從左至右)。
標(biāo)識(shí)符(也被稱為名字)是由以下詞法定義描述的。
Python 語言中的標(biāo)識(shí)符語法基于 Unicode 標(biāo)準(zhǔn)附件 UAX-31,闡述變化定義如下;也可以點(diǎn)擊 PEP 3131 獲得更詳細(xì)的信息。
在 ASCII 范圍內(nèi)( U+0001..U+007F ),合法的標(biāo)識(shí)符字符與 Python 2.X版本中是一致的:大寫及小寫字母的A到Z,下劃線_以及數(shù)字從0到9(第一個(gè)字符除外)。
Python 3.0版本引入了 ASCII 范圍以外其他的字符(詳見 PEP 3131 )。這些字符使用包含在 unicodedata 模塊中的 Unicode 字符數(shù)據(jù)庫版本進(jìn)行分類。
標(biāo)識(shí)符的長度是沒有限制的。
identifier ::= xid_start xid_continue
id_start ::= <all characters in general categories Lu, Ll, Lt, Lm, Lo, Nl, the underscore, and characters with the Other_ID_Start property>
id_continue ::= <all characters in id_start , plus characters in the categories Mn, Mc, Nd, Pc and others with the Other_ID_Continue property>
xid_start ::= <all characters in id_start whose NFKC normalization is in "id_start xid_continue">
xid_continue ::= <all characters in id_continue whose NFKC normalization is in "id_continue*">
上面提到的 Unicode 分類代碼分別代表:
解析器會(huì)將所有標(biāo)識(shí)符轉(zhuǎn)換為標(biāo)準(zhǔn)形式的 NFKC,標(biāo)識(shí)符的比較是基于 NFKC 的。
非標(biāo)準(zhǔn)的 HTML 文件列出了Unicode 4.1 中用到的所有合法標(biāo)識(shí)符,可以在http://www.dcl.hpi.uni-potsdam.de/home/loewis/table-3131.html中找到。
下面列出的標(biāo)識(shí)符被用作保留字,或者叫做該語言的關(guān)鍵字,這些保留字不能作為普通標(biāo)識(shí)符使用。這些關(guān)鍵字必須嚴(yán)格像下面一樣拼寫:
False class finally is return
None continue for lambda try
True def from nonlocal while
and del global not with
as elif if or yield
assert else import pass
break except in raise
不同類型的標(biāo)識(shí)符(關(guān)鍵字除外)都有其特殊含義。這些類型由前導(dǎo)和尾隨的下劃線字符模式確定:
_*
不能由from module import *導(dǎo)入。特殊字符_,在交互式解釋器中用于存儲(chǔ)上次估值的結(jié)果;它存儲(chǔ)在 builtins 模塊中。在非交互式模式下,字符 _ 沒有特殊含義,且是未定義的。詳見 The import statement 。
注意:
_常用于結(jié)合國際化;您也可以通過 gettext 獲取更多關(guān)于此問題的信息。
_ _*_ _
系統(tǒng)定義的名稱。這些名稱由解釋器及其實(shí)現(xiàn)定義(包括標(biāo)準(zhǔn)庫)。當(dāng)前系統(tǒng)名稱在 Special method names 部分討論。更多的系統(tǒng)名稱可能會(huì)在后續(xù)的 Python 版本中定義。在任意上下文中不遵循明確記錄的使用_ _ *_ _名字,會(huì)更容易產(chǎn)生錯(cuò)誤且不會(huì)提示任何告警。
_ _*
類私有名稱。在一個(gè)類定義的上下文中使用此類別中的名字時(shí),為避免基類及派生類中“私有”屬性之間的命名沖突,會(huì)被重寫為一種亂序形式。
文本是某些內(nèi)建類型常量的表示方法。
字符串文本由以下詞法定義描述:
stringliteral ::= [stringprefix] (shortstring | longstring)
stringprefix::= "r" | "u" | "R" | "U"
shortstring ::= "'" shortstringitem "'" | '"'
longstring ::= "'''" longstringitem "'''" | '"""'
shortstringitem ::= shortstringchar | stringescapeseq
longstringitem ::= longstringchar | stringescapeseq
shortstringchar ::= <any source character except "\" or newline or the quote>
longstringchar ::= <any source character except "\">
stringescapeseq ::= "\" <any source character>
bytesliteral ::= bytesprefix(shortbytes | longbytes)
bytesprefix::= "b" | "B" | "br" | "Br" | "bR" | "BR" | "rb" | "rB" | "Rb" | "RB"
shortbytes ::= "'" shortbytesitem "'" | '"'
longbytes ::= "'''" longbytesitem "'''" | '"""'
shortbytesitem ::= shortbyteschar | bytesescapeseq
longbytesitem ::= longbyteschar | bytesescapeseq
shortbyteschar ::= <any ASCII character except "\" or newline or the quote>
longbyteschar ::= <any ASCII character except "\">
bytesescapeseq ::= "\" <any ASCII character>
上面沒有表示出來的一個(gè)語法限制是在 stringprefix 或 bytesprefix 和字符串文本之間不允許出現(xiàn)空格。編碼聲明定義了源字符集;如果在源文件中未明確聲明編碼方式,則默認(rèn)使用 UTF-8 編碼;詳見 Encoding declarations 部分。
簡(jiǎn)單來說:兩種類型的字符串文本都可以用成對(duì)的單引號(hào)(')或雙引號(hào)(")括起來。它們也可以用成對(duì)的三個(gè)單引號(hào)或雙引號(hào)括起來(這些通常統(tǒng)稱為三重引號(hào)的字符串)。反斜線字符(\)稱為轉(zhuǎn)義字符,如果不轉(zhuǎn)義則其可能會(huì)產(chǎn)生歧義,例如換行符、反斜線本身或者引號(hào)。
字節(jié)文本總是以 ‘b’ 或 ‘B’ 開頭;它們會(huì)生成 bytes 類型實(shí)例而不是 str 類型。它們可能只包含 ASCII 字符;大于或等于 128 的數(shù)字必須使用轉(zhuǎn)義字符表示。
Python 3.3 中可能會(huì)再次在字符串文本前增加 u 字符前綴,用以簡(jiǎn)化 2.X 及 3.X 版本代碼的維護(hù)工作。
字符串文本及字節(jié)文本都可以(可選)加上字符前綴 r 或 R;這樣的字符串被稱為原始字符串,將反斜線視為原義字符。 這樣會(huì)導(dǎo)致在字符串文本中,原始串中的轉(zhuǎn)義字符 ‘\U’ 及 ‘\u’ 不會(huì)被特殊處理。考慮到 Python 2.X 版本中的原始 unicode 類型的特性與 3.X 版本差距較大,因此在 Python 3.X 版本中不再支持 ‘ur’ 語法。
3.3 版本新特性:原始字節(jié)文本的前綴 ‘rb’ 與 ‘br’ 意義相同。
3.3 版本新特性:舊版本中的(u ‘value’)被再次引入,用以簡(jiǎn)化 Python 2.X 及 3.X 版本的維護(hù)工作。更多的信息,請(qǐng)參閱 PEP 414。
在三重引用串中,允許出現(xiàn)未轉(zhuǎn)義的新行和引用字符(并被保留),除非三個(gè)連續(xù)的引用字符串中斷了該串。(引用字符是用于引用字符串的字符,如,’ 或 ”。)
如果給出一個(gè) ‘r’ 或 ‘R’,那么其含義就像標(biāo)準(zhǔn)C中的規(guī)則類似的解釋,轉(zhuǎn)義序列如下:
| 轉(zhuǎn)義序列 | 意義 | 注意事項(xiàng) |
|
\newline |
反斜線且忽略換行 |
|
|
\\ |
反斜線(\) |
|
|
\’ |
單引號(hào)(’) |
|
| \" | ?雙引號(hào)(”) |
|
| \a | ASCII Bell(BEL) |
|
| \b | ASCII 退格(BS) |
|
| \f | ASCII 換頁符(FF) |
|
| \n | ASCII 換行符(LF) |
|
| \r | ASCII 回車符(CR) |
|
| \t | ASCII 水平制表符(TAB) |
|
| \v | ASCII 垂直制表符(VT) |
|
| \ooo | 八進(jìn)制值為?ooo?的字符 | (1,3) |
| \xhh | 十六進(jìn)制值為?hh?的字符 | (2,3) |
字符串文本中的轉(zhuǎn)義序列規(guī)則如下:
| 轉(zhuǎn)義序列 | 意 ? ?義 | 注意事項(xiàng) |
| \N{name} | Unicode 數(shù)據(jù)庫中以 name 命名的字符 | (4) |
| \uxxxx | 16位16進(jìn)制字符值:xxxx | (5) |
| \Uxxxxxxxx | 32位16進(jìn)制字符值:xxxxxxxx | (6) |
注意:
不像標(biāo)準(zhǔn) C,所有不能被識(shí)別的轉(zhuǎn)義序列都保留在串中且不做改變,例如,反斜線會(huì)保留在結(jié)果中。(這個(gè)行為在調(diào)試過程中非常有用:如果輸入了一個(gè)錯(cuò)誤的轉(zhuǎn)義序列,在輸出結(jié)果中更容易識(shí)別出錯(cuò)誤。)此外,至關(guān)重要的是要注意轉(zhuǎn)義字符只能在字符串文本中起作用,在字節(jié)文本 類別中無法被識(shí)別。
即使在原始文本中,可以使用反斜線將引號(hào)轉(zhuǎn)義,但是反斜線本身會(huì)在結(jié)果中保留;比如 r"\"" 是一個(gè)由兩個(gè)字符組成的合法字符串:一個(gè)反斜線與一個(gè)雙引號(hào);但 r"\" 卻是一個(gè)非法字符串(即原始的字符串也不能以奇數(shù)個(gè)反斜杠結(jié)尾)。 具體而言,一個(gè)原始的文本不能以單個(gè)反斜杠結(jié)尾(由于反斜線會(huì)將跟在其后的引號(hào)轉(zhuǎn)義)。另外需要注意的是,如果一個(gè)反斜線跟在換行符后,反斜線與換行符會(huì)被當(dāng)做文本的兩個(gè)字符,而不是一個(gè)連續(xù)行。
多個(gè)相鄰的字符串文本或字節(jié)文本(由空白分隔),允許使用不同的引用習(xí)慣,并且其含義與連接在一起時(shí)是一樣的。因此, "hello" ‘world’ 與 "helloworld"是等價(jià)的。這個(gè)特性可以用來減少反斜線的使用數(shù)量,可以很方便的將一個(gè)長字符串分隔在多行上,甚至可以在字符串的某一部分添加注釋,例如:
re.compile("[A-Za-z_]" # letter or underscore
"[A-Za-z0-9_]*" # letter, digit or underscore
)
需要注意的是,這個(gè)特性是定義在句法層次上的,但是是在編譯時(shí)實(shí)現(xiàn)的。在運(yùn)行時(shí)連接串必須使用 ‘+’ 運(yùn)算符。并且不同的引用字符可以混用,甚至可以將原始串與三重引用串混合使用。
總共有三種類型數(shù)字文本:整型、浮點(diǎn)型以及虛數(shù)型。不存在復(fù)數(shù)文本(復(fù)數(shù)可以由一個(gè)實(shí)數(shù)加一個(gè)虛數(shù)的形式給出)。
注意,數(shù)字文本不包含符號(hào)(正負(fù)號(hào));像 -1 實(shí)際上是一個(gè)組合了一元運(yùn)算符 '-' 和數(shù)字 1 的表達(dá)式。
整型文本描述的詞法定義如下:
integer::= decimalinteger | octinteger | hexinteger | bininteger
decimalinteger ::= nonzerodigit digit* | "0"+
nonzerodigit ::= "1"..."9"
digit ::= "0"..."9"
octinteger ::= "0" ("o" | "O") octdigit+
hexinteger ::= "0" ("x" | "X") hexdigit+
bininteger ::= "0" ("b" | "B") bindigit+
octdigit ::= "0"..."7"
hexdigit ::= digit | "a"..."f" | "A"..."F"
bindigit ::= "0" | "1"
除了可以存儲(chǔ)在可用內(nèi)存中外,對(duì)整型文本的長度是沒有限制的。
注意,不允許在非零的小數(shù)前加零。這是為了消除與 Python 3.0 之前版本中使用的 C 風(fēng)格八進(jìn)制文本的歧義。
整型文本的例子如下:
7 2147483647 0o177 0b100110111
3 79228162514264337593543950336 0o377 0x100000000
79228162514264337593543950336 0xdeadbeef
浮點(diǎn)型文本描述的詞法定義如下:
floatnumber ::= pointfloat | exponentfloat
pointfloat ::= [intpart] fraction | intpart "."
exponentfloat ::= (intpart | pointfloat) exponent
intpart ::= digit+
fraction ::= "." digit+
exponent ::= ("e" | "E") ["+" | "-"] digit+
注意,浮點(diǎn)型文本的整數(shù)部分及指數(shù)部分都使用十進(jìn)制表示。比如, 077e010 是合法字符,并且與 77e10 等價(jià)。浮點(diǎn)型文本允許的范圍是依賴于實(shí)現(xiàn)的。一些浮點(diǎn)型文本的例子如下:
3.14 10. .001 1e100 3.14e-10 0e0
注意,數(shù)字文本不包含符號(hào)(正負(fù)號(hào));像 -1 實(shí)際上是由一元運(yùn)算符 - 和文本 1 組成。
虛數(shù)文本描述的詞法定義如下:
imagnumber ::= (floatnumber | intpart) ("j" | "J")
虛數(shù)是一個(gè)實(shí)部為零的復(fù)數(shù),復(fù)數(shù)代表一對(duì)有著相同取值范圍限制的浮點(diǎn)數(shù)對(duì)。為了創(chuàng)建一個(gè)實(shí)部非零的復(fù)數(shù),可以對(duì)它增加一個(gè)浮點(diǎn)數(shù),例如(3+4j)。下面是一些關(guān)于虛數(shù)文本的例子:
3.14j 10.j 10j .001j 1e100j 3.14e-10j
下面列出的符號(hào)為操作符:
+ - * ** / // %
<< >> & | ^ ~
< > <= >= == !=
以下符號(hào)用作語法上的分隔符:
( ) [ ] { }
, : . ; @ = ->
+= -= *= /= //= %=
&= |= ^= >>= <<= **=
句號(hào)也可以在浮點(diǎn)型及虛數(shù)文本中出現(xiàn),一個(gè)連續(xù)三個(gè)句號(hào)的序列具有特殊含義,代表了一個(gè)片段中的省略部分。該列表的后半部分,即參數(shù)化賦值運(yùn)算符,在詞法上是作為分隔符處理,但也執(zhí)行運(yùn)算。
下面列出的 ASCII 字符作為其他符號(hào)的一部分具有特殊含義,或者對(duì)于詞法分析器具有重要意義。
' " # \
下面列出的 ASCII 字符沒有在 Python 中使用。當(dāng)它們出現(xiàn)在字符串文本及注釋之外時(shí)就認(rèn)為是非法的。
$ ? `