2. 词法分析
Python 会将读取的程序文本转为 Unicode 码点;源文件的文本编码可由编码声明指定,默认为 UTF-8,详情见 PEP 3120。如果源文件无法被解码,将会引发 。
一个 Python 程序可分为许多 逻辑行。
逻辑行的结束是以 NEWLINE 形符表示的。语句不能跨越逻辑行的边界,除非其语法允许包含 NEWLINE (例如复合语句可由多行子语句组成)。一个逻辑行可由一个或多个 物理行 按照明确或隐含的 行拼接 规则构成。
2.1.2. 物理行
物理行是以一个行终止序列结束的字符序列。在源文件和字符串中,可以使用任何标准平台上的行终止序列 - Unix 所用的 ASCII 字符 LF (换行), Windows 所用的 ASCII 字符序列 CR LF (回车加换行), 或者旧 Macintosh 所用的 ASCII 字符 CR (回车)。所有这些形式均可使用,无论具体平台。输入的结束也会被作为最后一个物理行的隐含终止标志。
当嵌入 Python 时,源码字符串传入 Python API 应使用标准 C 的传统换行符 (即 ,表示 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.]+)
,这条注释会被作为编码声明来处理;上述表达式的第一组指定了源码文件的编码。编码声明必须独占一行。如果它是在第二行,则第一行也必须是注释。推荐的编码声明形式如下
这也是 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 形符。隐式拼接的行也可以出现于三引号字符串中 (见下);此情况下这些行不允许带有注释。
一个只包含空格符,制表符,进纸符或者注释的逻辑行会被忽略 (即不生成 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,还存在以下类别的形符: 标识符, 关键字, 字面值, 运算符 以及 分隔符。 空白字符 (之前讨论过的行终止符除外) 不属于形符,而是用来分隔形符。如果存在二义性,将从左至右读取尽可能长的合法字符串组成一个形符。
标识符 (或者叫做 名称) 由以下词法定义进行描述。
Python 中的标识符语法是基于 Unicode 标准附件 UAX-31,并加入了下文所定义的细化与修改;更多细节还可参见 。
在 ASCII 范围内 (U+0001..U+007F),可用于标识符的字符与 Python 2.x 一致: 大写和小写字母 A
至 Z
,下划线 _
以及数字 0
至 9
,但不可以数字打头。
Python 3.0 引入了 ASCII 范围以外的额外字符 (见 PEP 3131)。这些字符的分类使用包含于 模块中的 Unicode 字符数据库版本。
标识符的长度没有限制。对大小写敏感。
- 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 whose NFKC normalization is in "id_start xid_continue*">
- xid_continue ::= <all characters in id_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 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 *
导入。特殊标识符 _
在交互式解释器中被用来存放最近一次求值结果;它保存在 模块中。当不处于交互模式时,_
无特殊含义也没有预定义。参见 The import statement。
注解
__*__
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 ::= [](shortstring | )
- stringprefix ::= "r" | "u" | "R" | "U" | "f" | "F"
- | "fr" | "Fr" | "fR" | "FR" | "rf" | "rF" | "Rf" | "RF"
- shortstring ::= "'" shortstringitem* "'" | '"' * '"'
- longstring ::= "'''" longstringitem* "'''" | '"""' * '"""'
- shortstringitem ::= shortstringchar |
- longstringitem ::= longstringchar |
- shortstringchar ::= <any source character except "\" or newline or the quote>
- longstringchar ::= <any source character except "\">
- stringescapeseq ::= "\" <any source character>
这些条目中未提及的一个语法限制是 stringprefix 或 与字面值的剩余部分之间不允许有空白。源字符集是由编码声明定义的;如果源文件中没有编码声明则默认为 UTF-8;参见 编码声明。
自然语言描述: 两种字面值都可以用成对单引号 () 或双引号 ("
) 来标示首尾。它们也可以用成对的连续三个单引号或双引号来标示首尾 (这通常被称为 三引号字符串)。反斜杠 (\
) 字符被用来对特殊含义的字符进行转义,例如换行,反斜杠本身或是引号等字符。
字节串字面值总是带有前缀 'b'
或 'B'
;它们生成 类型而非 str 类型的实例。它们只能包含 ASCII 字符;字节对应数值在128及以上必须以转义形式来表示。
字符串和字节串字面值都可以带有前缀 'r'
或 'R'
;这种字符串被称为 原始字符串 其中的反斜杠会被当作其本身的字面字符来处理。因此在原始字符串字面值中,'\U'
和 '\u'
转义形式不会被特殊对待。由于 Python 2.x 的原始统一码字面值的特性与 Python 3.x 不一致,'ur'
语法已不再被支持。
3.3 新版功能: 新加入了表示原始字节串的 'rb'
前缀,与 'br'
的意义相同。
3.3 新版功能: 对旧式统一码字面值 (u'value'
) 的支持被重新引入以简化 Python 2.x 和 3.x 代码库的同步维护。详情见 。
包含 'f'
或 'F'
前缀的字符串字面值称为 格式化字符串字面值;参见 格式化字符串字面值。'f'
可与 'r'
连用,但不能与 'b'
或 'u'
连用,因此存在原始格式化字符串,但不存在格式化字节串字面值。
在三引号字面值中,允许存在未经转义的换行和引号 (并原样保留),除非是未经转义的连续三引号,这标示着字面值的结束。 (“引号” 是用来标示字面值的字符,即 '
或 "
。)
除非带有 'r'
或 'R'
前缀,字符串和字节串字面值中的转义序列会基于类似标准 C 中的转义规则来解读。可用的转义序列如下:
仅在字符串字面值中可用的转义序列如下:
转义序列 | 含义 | 注释 |
---|---|---|
| Unicode 数据库中名称为 name 的字符 | (4) |
16位十六进制数 xxxx 码位的字符 | (5) | |
| 32位16进制数 xxxxxxxx 码位的字符 | (6) |
注释:
与标准 C 一致,接受最多三个八进制数码。
与标准 C 不同,要求必须为两个十六进制数码。
在字节串字面值中,十六进制数和八进制数转义码以相应数值代表每个字节。在字符串字面值中,这些转义码以相应数值代表每个 Unicode 字符。
在 3.3 版更改: 加入了对别名 的支持。
要求必须为四个十六进制数码。
此方式可用来表示任意 Unicode 字符。要求必须为八个十六进制数码。
与标准 C 不同,所有无法识别的转义序列将原样保留在字符串中,也就是说,反斜杠会在结果中保留。(这种方式在调试时很有用: 如果输错了一个转义序列,更容易在输出结果中识别错误。) 另外要注意的一个关键点是:专用于字符串字面值中的转义序列如果在字节串字面值中出现,会被归类为无法识别的转义序列。
即使在原始字面值中,引号也可以加上反斜杠转义符,但反斜杠会保留在输出结果中;例如 r"\""
是一个有效的字符串字面值,包含两个字符: 一个反斜杠和一个双引号;而 r"\"
不是一个有效的字符串字面值 (即便是原始字符串也不能以奇数个反斜杠结束)。特别地,一个原始字面值不能以单个反斜杠结束 (因为此反斜杠会转义其后的引号字符)。还要注意一个反斜杠加一个换行在字面值中会被解释为两个字符,而 不是 一个连续行。
多个相邻的字符串或字节串字面值 (以空白符分隔),所用的引号可以彼此不同,其含义等同于全部拼接为一体。因此, "hello" 'world'
等同于 "helloworld"
。此特性可以减少反斜杠的使用,以方便地将很长的字符串分成多个物理行,甚至每部分字符串还可分别加注释,例如:
re.compile("[A-Za-z_]" # letter or underscore
"[A-Za-z0-9_]*" # letter, digit or underscore
)
注意此特性是在句法层面定义的,但是在编译时实现。在运行时拼接字符串表达式必须使用 ‘+’ 运算符。还要注意字面值拼接时每个部分可以使用不同的引号风格 (甚至混合使用原始字符串和三引号字符串),格式化字符串字面值也可与普通字符串字面值拼接。
2.4.3. 格式化字符串字面值
3.6 新版功能.
格式化字符串字面值 或称 f-string 是带有 'f'
或 'F'
前缀的字符串字面值。这种字符串可包含替换字段,即以 {}
标示的表达式。而其他字符串字面值总是一个常量,格式化字符串字面值实际上是会在运行时被求值的表达式。
转义序列会像在普通字符串字面值中一样被解码 (除非字面值还被标示为原始字符串)。解码之后,字符串内容所用的语法如下:
- f_string ::= ( | "{{" | "}}" | replacement_field)*
- replacement_field ::= "{" ["!" conversion] [":" ] "}"
- f_expression ::= (conditional_expression | "*" )
- ("," conditional_expression | "," "*" )* [","]
- | yield_expression
- conversion ::= "s" | "r" | "a"
- format_spec ::= ( | NULL | replacement_field)*
- literal_char ::= <any code point except "{", "}" or NULL>
字符串在花括号以外的部分按其字面值处理,除了双重花括号 '{{'
或 会被替换为相应的单个花括号。单个左花括号 '{'
标示一个替换字段,它以一个 Python 表达式打头,表达式之后可能有一个以叹号 '!'
标示的转换字段。之后还可能带有一个以冒号 ':'
标示的格式说明符。替换字段以一个右花括号 '}'
作为结束。
格式化字符串字面值中的表达式会被当作正常的包含在圆括号中的 Python 表达式一样处理,但有少数例外。空表达式不被允许, 表达式必须显式地加上圆括号。替换表达式可以包含换行 (例如在三引号字符串中),但是不能包含注释。每个表达式会在格式化字符串字面值所包含的位置按照从左至右的顺序被求值。
An await expression and comprehensions containing an clause are illegal in the expression in formatted string literals. (The reason is a problem with the implementation — this restriction is lifted in Python 3.7).
如果指定了转换符,表达式的求值结果会先转换再格式化。转换符 '!s'
即对结果调用 str(),'!r'
为调用 ,而 '!a'
为调用 ascii()。
在此之后结果会使用 协议进行格式化。格式说明符会被传入表达式或转换结果的 __format__() 方法。如果省略格式说明符则会传入一个空字符串。然后格式化结果会包含在整个字符串最终的值当中。
顶层的格式说明符可以包含有嵌套的替换字段。这些嵌套字段也可以包含有自己的转换字段和 ,但不可再包含更深层嵌套的替换字段。这里的 格式说明符微型语言 与字符串 .format() 方法所使用的相同。
格式化字符串字面值可以拼接,但是一个替换字段不能拆分到多个字面值。
一些格式化字符串字面值的示例:
>>> name = "Fred"
"He said his name is 'Fred'."
>>> f"He said his name is {repr(name)}." # repr() is equivalent to !r
"He said his name is 'Fred'."
>>> width = 10
>>> precision = 4
>>> value = decimal.Decimal("12.34567")
>>> f"result: {value:{width}.{precision}}" # nested fields
'result: 12.35'
>>> today = datetime(year=2017, month=1, day=27)
>>> f"{today:%B %d, %Y}" # using date format specifier
'January 27, 2017'
>>> number = 1024
>>> f"{number:#0x}" # using integer format specifier
'0x400'
与正常字符串字面值采用相同语法导致的一个结果就是替换字段中的字符不能与外部的格式化字符串字面值所用的引号相冲突:
f"abc {a["x"]} def" # error: outer string literal ended prematurely
f"abc {a['x']} def" # workaround: use different quoting
格式表达式中不允许有反斜杠,这会引发错误:
f"newline: {ord('\n')}" # raises SyntaxError
想包含需要用反斜杠转义的值,可以创建一个临时变量。
>>> newline = ord('\n')
>>> f"newline: {newline}"
'newline: 10'
格式化字符串字面值不可用作文档字符串,即便其中没有包含表达式。
>>> def foo():
... f"Not a docstring"
...
>>> foo.__doc__ is None
True
另请参见 了解加入格式化字符串字面值的提议,以及使用了相关的格式字符串机制的 str.format()。
2.4.4. 数字字面值
数字字面值有三种类型: 整型数、浮点数和虚数。没有专门的复数字面值 (复数可由一个实数加一个虚数合成)。
注意数字字面值并不包含正负号;-1
这样的负数实际上是由单目运算符 ‘-
‘ 和字面值 1
合成的。
2.4.5. 整型数字面值
整型数字面值由以下词法定义进行描述:
- integer ::= | bininteger | | hexinteger
- decinteger ::= (["_"] digit)* | "0"+ (["_"] "0")*
- bininteger ::= "0" ("b" | "B") (["_"] )+
- octinteger ::= "0" ("o" | "O") (["_"] octdigit)+
- hexinteger ::= "0" ("x" | "X") (["_"] )+
- nonzerodigit ::= "1"..."9"
- digit ::= "0"..."9"
- bindigit ::= "0" | "1"
- octdigit ::= "0"..."7"
- hexdigit ::= digit | "a"..."f" | "A"..."F"
整型数字面值的长度没有限制,能一直大到占满可用内存。
在确定数字大小时字面值中的下划线会被忽略。它们可用来将数码分组以提高可读性。一个下划线可放在数码之间,也可放在基数说明符例如 0x
之后。
注意非零的十进制数开头不允许有额外的零。这是为了避免与 Python 在版本 3.0 之前所使用的 C 风格八进制字面值相混淆。
一些整型数字面值的示例如下:
在 3.6 版更改: 允许在字面值中使用下划线进行分组。
2.4.6. 浮点数字面值
浮点数字面值由以下词法定义进行描述:
- floatnumber ::= pointfloat |
- pointfloat ::= [digitpart] | digitpart "."
- exponentfloat ::= ( | pointfloat)
- digitpart ::= digit (["_"] )*
- fraction ::= "." digitpart
- exponent ::= ("e" | "E") ["+" | "-"]
注意整型数部分和指数部分在解析时总是以 10 为基数。例如,077e010
是合法的,且表示的数值与 77e10
相同。浮点数字面值允许的范围依赖于具体实现。对于整型数字面值,支持以下划线进行分组。
一些浮点数字面值的示例如下:
3.14 10. .001 1e100 3.14e-10 0e0 3.14_15_93
在 3.6 版更改: 允许在字面值中使用下划线进行分组。
2.4.7. 虚数字面值
虚数字面值由以下词法定义进行描述:
- imagnumber ::= ( | digitpart) ("j" | "J")
一个虚数字面值将生成一个实部为 0.0 的复数。复数是以一对浮点数来表示的,它们的取值范围相同。要创建一个实部不为零的复数,就加上一个浮点数,例如 (3+4j)
。一些虚数字面值的示例如下:
3.14j 10.j 10j .001j 1e100j 3.14e-10j 3.14_15_93j
以下形符属于运算符:
+ - * ** / // % @
<< >> & | ^ ~
< > <= >= == !=
2.6. 分隔符
以下形符在语法中归类为分隔符:
( ) [ ] { }
, : . ; @ = ->
+= -= *= /= //= %= @=
&= |= ^= >>= <<= **=
句点也可出现于浮点数和虚数字面值中。连续三个句点有表示一个省略符的特殊含义。以上列表的后半部分为增强赋值操作符,在词法中作为分隔符,但也起到运算作用。
以下可打印 ASCII 字符作为其他形符的组成部分时具有特殊含义,或是对词法分析器有重要意义:
以下可打印 ASCII 字符不在 Python 词法中使用。如果出现于字符串字面值和注释之外将无条件地引发错误:
$ ? `
备注