2. 词法分析¶
Python 程序由一个 解析器 读取。输入到解析器的是一个由 词法分析器 所生成的 形符 流,本章将描述词法分析器是如何将一个文件拆分为一个个形符的。
Python 会将读取的程序文本转为 Unicode 码点;源文件的文本编码可由编码声明指定,默认为 UTF-8,详情见 PEP 3120。如果源文件无法被解码,将会引发 SyntaxError
。
2.1. 行结构¶
一个 Python 程序可分为许多 逻辑行。
2.1.1. 逻辑行¶
逻辑行的结束是以 NEWLINE 形符表示的。语句不能跨越逻辑行的边界,除非其语法允许包含 NEWLINE (例如复合语句可由多行子语句组成)。一个逻辑行可由一个或多个 物理行 按照明确或隐含的 行拼接 规则构成。
2.1.2. 物理行¶
A physical line is a sequence of characters terminated by an end-of-line sequence. In source files, any of the standard platform line termination sequences can be used - the Unix form using ASCII LF (linefeed), the Windows form using the ASCII sequence CR LF (return followed by linefeed), or the old Macintosh form using the ASCII CR (return) character. All of these forms can be used equally, regardless of platform.
当嵌入 Python 时,源码字符串传入 Python API 应使用标准 C 的传统换行符 (即 \n
,表示 ASCII 字符 LF 作为行终止标志)。
2.1.3. 注释¶
A comment starts with a hash character (#
) that is not part of a string
literal, and ends at the end of the physical line. A comment signifies the end
of the logical line unless the implicit line joining rules are invoked. Comments
are ignored by the syntax; they are not tokens.
2.1.4. 编码声明¶
如果一条注释位于 Python 脚本的第一或第二行,并且匹配正则表达式 coding[=:]\s*([-\w.]+)
,这条注释会被作为编码声明来处理;上述表达式的第一组指定了源码文件的编码。编码声明必须独占一行。如果它是在第二行,则第一行也必须是注释。推荐的编码声明形式如下
# -*- coding: <encoding-name> -*-
这也是 GNU Emacs 认可的形式,以及
# vim:fileencoding=<encoding-name>
这是 Bram Moolenaar 的 VIM 认可的形式。
如果没有编码声明,则默认编码为 UTF-8。此外,如果文件的首字节为 UTF-8 字节顺序标志 (b'\xef\xbb\xbf'
),文件编码也声明为 UTF-8 (这是 Microsoft 的 notepad 等软件支持的形式)。
编码声明指定的编码名称必须是 Python 所认可的编码。所有词法分析将使用此编码,包括语义字符串、注释和标识符。
2.1.5. 显式的行拼接¶
两个或更多个物理行可使用反斜杠字符 (\
) 拼接为一个逻辑行,规则如下: 当一个物理行以一个不在字符串或注释内的反斜杠结尾时,它将与下一行拼接构成一个单独的逻辑行,反斜杠及其后的换行符会被删除。例如:
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
以反斜杠结束的行不能带有注释。反斜杠不能用来拼接注释。反斜杠不能用来拼接形符,字符串除外 (即原文字符串以外的形符不能用反斜杠分隔到两个物理行)。不允许有原文字符串以外的反斜杠存在于物理行的其他位置。
2.1.6. 隐式的行拼接¶
圆括号、方括号或花括号以内的表达式允许分成多个物理行,无需使用反斜杠。例如:
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
隐式的行拼接可以带有注释。后续行的缩进不影响程序结构。后续行也允许为空白行。隐式拼接的行之间不会有 NEWLINE 形符。隐式拼接的行也可以出现于三引号字符串中 (见下);此情况下这些行不允许带有注释。
2.1.7. 空白行¶
一个只包含空格符,制表符,进纸符或者注释的逻辑行会被忽略 (即不生成 NEWLINE 形符)。在交互模式输入语句时,对空白行的处理可能会因读取-求值-打印循环的具体实现方式而存在差异。在标准交互模式解释器中,一个完全空白的逻辑行 (即连空格或注释都没有) 将会结束一条多行复合语句。
2.1.8. 缩进¶
一个逻辑行开头处的空白 (空格符和制表符) 被用来计算该行的缩进等级,以决定语句段落的组织结构。
制表符会被 (从左至右) 替换为一至八个空格,这样缩进的空格总数为八的倍数 (这是为了与 Unix 所用的规则一致)。首个非空白字符之前的空格总数将确定该行的缩进层次。一个缩进不可使用反斜杠进行多行拼接;首个反斜杠之前的空格将确定缩进层次。
在一个源文件中如果混合使用制表符和空格符缩进,并使得确定缩进层次需要依赖于制表符对应的空格数量设置,则被视为不合规则;此情况将会引发 TabError
。
跨平台兼容性注释: 由于非 UNIX 平台上文本编辑器本身的特性,在一个源文件中混合使用制表符和空格符是不明智的。另外也要注意不同平台还可能会显式地限制最大缩进层级。
行首有时可能会有一个进纸符;它在上述缩进层级计算中会被忽略。处于行首空格内其他位置的进纸符的效果未定义 (例如它可能导致空格计数重置为零)。
多个连续行各自的缩进层级将会被放入一个堆栈用来生成 INDENT 和 DEDENT 形符,具体说明如下。
在读取文件的第一行之前,先向堆栈推入一个零值;它将不再被弹出。被推入栈的层级数值从底至顶持续增加。每个逻辑行开头的行缩进层级将与栈顶行比较。如果相同,则不做处理。如果新行层级较高,则会被推入栈顶,并生成一个 INDENT 形符。如果新行层级较低,则 应当 是栈中的层级数值之一;栈中高于该层级的所有数值都将被弹出,每弹出一级数值生成一个 DEDENT 形符。在文件末尾,栈中剩余的每个大于零的数值生成一个 DEDENT 形符。
这是一个正确 (但令人迷惑) 的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
以下示例显示了各种缩进错误:
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
(实际上,前三个错误会被解析器发现;只有最后一个错误是由词法分析器发现的 — return r
的缩进无法匹配弹出栈的缩进层级。)
2.1.9. 形符之间的空白¶
除非是在逻辑行的开头或字符串内,空格符、制表符和进纸符等空白符都同样可以用来分隔形符。如果两个形符彼此相连会被解析为一个不同的形符,则需要使用空白来分隔 (例如 ab 是一个形符,而 a b 是两个形符)。
2.2. 其他形符¶
除了 NEWLINE, INDENT 和 DEDENT,还存在以下类别的形符: 标识符, 关键字, 字面值, 运算符 以及 分隔符。 空白字符 (之前讨论过的行终止符除外) 不属于形符,而是用来分隔形符。如果存在二义性,将从左至右读取尽可能长的合法字符串组成一个形符。
2.3. 标识符和关键字¶
标识符 (或者叫做 名称) 由以下词法定义进行描述。
Python 中的标识符语法是基于 Unicode 标准附件 UAX-31,并加入了下文所定义的细化与修改;更多细节还可参见 PEP 3131 。
在 ASCII 范围内 (U+0001..U+007F),可用于标识符的字符与 Python 2.x 一致: 大写和小写字母 A
至 Z
,下划线 _
以及数字 0
至 9
,但不可以数字打头。
Python 3.0 引入了 ASCII 范围以外的额外字符 (见 PEP 3131)。这些字符的分类使用包含于 unicodedata
模块中的 Unicode 字符数据库版本。
标识符的长度没有限制。对大小写敏感。
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 inid_start
, plus characters in the categories Mn, Mc, Nd, Pc and others with the Other_ID_Continue property> xid_start ::= <all characters inid_start
whose NFKC normalization is in "id_start xid_continue*"> xid_continue ::= <all characters inid_continue
whose NFKC normalization is in "id_continue*">
上文所用 Unicode 类别码的含义:
- Lu - 大写字母
- Ll - 小写字母
- Lt - 词首大写字母
- Lm - 修饰字母
- Lo - 其他字母
- Nl - 字母数字
- Mn - 非空白标识
- Mc - 含空白标识
- Nd - 十进制数字
- Pc - 连接标点
- Other_ID_Start - explicit list of characters in PropList.txt to support backwards compatibility
- Other_ID_Continue - 同上
所有标识符在解析时会被转换为规范形式 NFKC;标识符的比较都是基于 NFKC。
A non-normative HTML file listing all valid identifier characters for Unicode 4.1 can be found at https://www.dcl.hpi.uni-potsdam.de/home/loewis/table-3131.html.
2.3.1. 关键字¶
以下标识符被作为语言的保留字或称 关键字,不可被用作普通标识符。关键字的拼写必须与这里列出的完全一致。
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
2.3.2. 保留的标识符类¶
某些标识符类 (除了关键字) 具有特殊的含义。这些标识符类的命名模式是以下划线字符打头和结尾:
_*
不会被
from module import *
导入。特殊标识符_
在交互式解释器中被用来存放最近一次求值结果;它保存在builtins
模块中。当不处于交互模式时,_
无特殊含义也没有预定义。参见 The import statement。備註
_
作为名称常用于连接国际化文本;请参看gettext
模块文档了解有关此约定的详情。__*__
- System-defined names. These names are defined by the interpreter and its
implementation (including the standard library). Current system names are
discussed in the 特殊方法名称 section and elsewhere. More will likely
be defined in future versions of Python. Any use of
__*__
names, in any context, that does not follow explicitly documented use, is subject to breakage without warning. __*
- 类的私有名称。这种名称在类定义中使用时,会以一种混合形式重写以避免在基类及派生类的 「私有」 属性之间出现名称冲突。参见 标识符(名称)。
2.4. 字面值¶
字面值用于表示一些内置类型的常量。
2.4.1. 字符串和字节串字面值¶
字符串字面值由以下词法定义进行描述:
stringliteral ::= [stringprefix
](shortstring
|longstring
) stringprefix ::= "r" | "u" | "R" | "U" shortstring ::= "'"shortstringitem
* "'" | '"'shortstringitem
* '"' longstring ::= "'''"longstringitem
* "'''" | '"""'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
* "'" | '"'shortbytesitem
* '"' longbytes ::= "'''"longbytesitem
* "'''" | '"""'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>
这些条目中未提及的一个语法限制是 stringprefix
或 bytesprefix
与字面值的剩余部分之间不允许有空白。源字符集是由编码声明定义的;如果源文件中没有编码声明则默认为 UTF-8;参见 编码声明。
自然语言描述: 两种字面值都可以用成对单引号 ('
) 或双引号 ("
) 来标示首尾。它们也可以用成对的连续三个单引号或双引号来标示首尾 (这通常被称为 三引号字符串)。反斜杠 (\
) 字符被用来对特殊含义的字符进行转义,例如换行,反斜杠本身或是引号等字符。
字节串字面值总是带有前缀 'b'
或 'B'
;它们生成 bytes
类型而非 str
类型的实例。它们只能包含 ASCII 字符;字节对应数值在128及以上必须以转义形式来表示。
As of Python 3.3 it is possible again to prefix string literals with a
u
prefix to simplify maintenance of dual 2.x and 3.x codebases.
字符串和字节串字面值都可以带有前缀 'r'
或 'R'
;这种字符串被称为 原始字符串 其中的反斜杠会被当作其本身的字面字符来处理。因此在原始字符串字面值中,'\U'
和 '\u'
转义形式不会被特殊对待。由于 Python 2.x 的原始统一码字面值的特性与 Python 3.x 不一致,'ur'
语法已不再被支持。
3.3 版新加入: 新加入了表示原始字节串的 'rb'
前缀,与 'br'
的意义相同。
3.3 版新加入: 对旧式统一码字面值 (u'value'
) 的支持被重新引入以简化 Python 2.x 和 3.x 代码库的同步维护。详情见 PEP 414。
在三引号字面值中,允许存在未经转义的换行和引号 (并原样保留),除非是未经转义的连续三引号,这标示着字面值的结束。 (「引号」 是用来标示字面值的字符,即 '
或 "
。)
除非带有 'r'
或 'R'
前缀,字符串和字节串字面值中的转义序列会基于类似标准 C 中的转义规则来解读。可用的转义序列如下:
转义序列 | 含义 | 註解 |
---|---|---|
\newline |
反斜杠加换行全被忽略 | |
\\ |
反斜杠 (\ ) |
|
\' |
单引号 (' ) |
|
\" |
双引号 (" ) |
|
\a |
ASCII 响铃 (BEL) | |
\b |
ASCII 退格 (BS) | |
\f |
ASCII 进纸 (FF) | |
\n |
ASCII 换行 (LF) | |
\r |
ASCII 回车 (CR) | |
\t |
ASCII 水平制表 (TAB) | |
\v |
ASCII 垂直制表 (VT) | |
\ooo |
八进制数 ooo 码位的字符 | (1,3) |
\xhh |
十六进制数 hh 码位的字符 | (2,3) |
仅在字符串字面值中可用的转义序列如下:
转义序列 | 含义 | 註解 |
---|---|---|
\N{name} |
Unicode 数据库中名称为 name 的字符 | (4) |
\uxxxx |
16位十六进制数 xxxx 码位的字符 | (5) |
\Uxxxxxxxx |
32位16进制数 xxxxxxxx 码位的字符 | (6) |
註解:
与标准 C 一致,接受最多三个八进制数码。
与标准 C 不同,要求必须为两个十六进制数码。
在字节串字面值中,十六进制数和八进制数转义码以相应数值代表每个字节。在字符串字面值中,这些转义码以相应数值代表每个 Unicode 字符。
3.3 版更變: 加入了对别名 [1] 的支持。
要求必须为四个十六进制数码。
此方式可用来表示任意 Unicode 字符。要求必须为八个十六进制数码。
与标准 C 不同,所有无法识别的转义序列将原样保留在字符串中,也就是说,反斜杠会在结果中保留。(这种方式在调试时很有用: 如果输错了一个转义序列,更容易在输出结果中识别错误。) 另外要注意的一个关键点是:专用于字符串字面值中的转义序列如果在字节串字面值中出现,会被归类为无法识别的转义序列。
即使在原始字面值中,引号也可以加上反斜杠转义符,但反斜杠会保留在输出结果中;例如 r"\""
是一个有效的字符串字面值,包含两个字符: 一个反斜杠和一个双引号;而 r"\"
不是一个有效的字符串字面值 (即便是原始字符串也不能以奇数个反斜杠结束)。特别地,一个原始字面值不能以单个反斜杠结束 (因为此反斜杠会转义其后的引号字符)。还要注意一个反斜杠加一个换行在字面值中会被解释为两个字符,而 不是 一个连续行。
2.4.2. 字符串字面值拼接¶
多个相邻的字符串或字节串字面值 (以空白符分隔),所用的引号可以彼此不同,其含义等同于全部拼接为一体。因此, "hello" 'world'
等同于 "helloworld"
。此特性可以减少反斜杠的使用,以方便地将很长的字符串分成多个物理行,甚至每部分字符串还可分别加注释,例如:
re.compile("[A-Za-z_]" # letter or underscore
"[A-Za-z0-9_]*" # letter, digit or underscore
)
Note that this feature is defined at the syntactical level, but implemented at compile time. The 『+』 operator must be used to concatenate string expressions at run time. Also note that literal concatenation can use different quoting styles for each component (even mixing raw strings and triple quoted strings).
2.4.3. 数字字面值¶
数字字面值有三种类型: 整型数、浮点数和虚数。没有专门的复数字面值 (复数可由一个实数加一个虚数合成)。
注意数字字面值并不包含正负号;-1
这样的负数实际上是由单目运算符 『-
『 和字面值 1
合成的。
2.4.4. 整型数字面值¶
整型数字面值由以下词法定义进行描述:
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"
整型数字面值的长度没有限制,能一直大到占满可用内存。
注意非零的十进制数开头不允许有额外的零。这是为了避免与 Python 在版本 3.0 之前所使用的 C 风格八进制字面值相混淆。
一些整型数字面值的示例如下:
7 2147483647 0o177 0b100110111
3 79228162514264337593543950336 0o377 0xdeadbeef
2.4.5. 浮点数字面值¶
浮点数字面值由以下词法定义进行描述:
floatnumber ::=pointfloat
|exponentfloat
pointfloat ::= [intpart
]fraction
|intpart
"." exponentfloat ::= (intpart
|pointfloat
)exponent
intpart ::=digit
+ fraction ::= "."digit
+ exponent ::= ("e" | "E") ["+" | "-"]digit
+
Note that the integer and exponent parts are always interpreted using radix 10.
For example, 077e010
is legal, and denotes the same number as 77e10
. The
allowed range of floating point literals is implementation-dependent. Some
examples of floating point literals:
3.14 10. .001 1e100 3.14e-10 0e0
Note that numeric literals do not include a sign; a phrase like -1
is
actually an expression composed of the unary operator -
and the literal
1
.
2.4.6. 虚数字面值¶
虚数字面值由以下词法定义进行描述:
imagnumber ::= (floatnumber
|intpart
) ("j" | "J")
一个虚数字面值将生成一个实部为 0.0 的复数。复数是以一对浮点数来表示的,它们的取值范围相同。要创建一个实部不为零的复数,就加上一个浮点数,例如 (3+4j)
。一些虚数字面值的示例如下:
3.14j 10.j 10j .001j 1e100j 3.14e-10j
2.6. 分隔符¶
以下形符在语法中归类为分隔符:
( ) [ ] { }
, : . ; @ = ->
+= -= *= /= //= %= @=
&= |= ^= >>= <<= **=
句点也可出现于浮点数和虚数字面值中。连续三个句点有表示一个省略符的特殊含义。以上列表的后半部分为增强赋值操作符,在词法中作为分隔符,但也起到运算作用。
以下可打印 ASCII 字符作为其他形符的组成部分时具有特殊含义,或是对词法分析器有重要意义:
' " # \
以下可打印 ASCII 字符不在 Python 词法中使用。如果出现于字符串字面值和注释之外将无条件地引发错误:
$ ? `
註解
[1] | http://www.unicode.org/Public/8.0.0/ucd/NameAliases.txt |