2. 词法分析 — Python 文档
2. 词法分析
Python 程序由 解析器 读取。 解析器的输入是 令牌流 ,由 词法分析器 生成。 本章描述词法分析器如何将文件分解为标记。
Python 将程序文本读取为 Unicode 代码点; 源文件的编码可以由编码声明给出,默认为 UTF-8,详情请参见 PEP 3120。 如果无法解码源文件,则会引发 SyntaxError。
2.1. 线路结构
一个 Python 程序被分成多个 逻辑行 。
2.1.1. 逻辑线
逻辑行的结尾由标记 NEWLINE 表示。 语句不能跨越逻辑行边界,除非语法允许 NEWLINE(例如,复合语句中的语句之间)。 通过遵循显式或隐式 行连接 规则,从一个或多个 物理行 构建逻辑行。
2.1.2. 物理线路
物理行是由行尾序列终止的字符序列。 在源文件和字符串中,可以使用任何标准的平台行终止序列 - 使用 ASCII LF(换行符)的 Unix 形式,使用 ASCII 序列 CR LF(返回后跟换行符)的 Windows 形式,或使用旧的 Macintosh 形式ASCII CR(返回)字符。 无论平台如何,所有这些形式都可以同等使用。 输入的结尾也用作最终物理行的隐式终止符。
嵌入 Python 时,源代码字符串应使用换行符的标准 C 约定传递给 Python API(表示 ASCII LF 的 \n
字符是行终止符)。
2.1.4. 编码声明
如果 Python 脚本的第一行或第二行中的注释与正则表达式 coding[=:]\s*([-\w.]+)
匹配,则将该注释作为编码声明处理; 此表达式的第一组命名源代码文件的编码。 编码声明必须单独出现一行。 如果是第二行,则第一行也必须是仅注释行。 编码表达式的推荐形式是
这也被 GNU Emacs 认可,以及
这被 Bram Moolenaar 的 VIM 认可。
如果未找到编码声明,则默认编码为 UTF-8。 此外,如果文件的第一个字节是 UTF-8 字节顺序标记 (b'\xef\xbb\xbf'
),则声明的文件编码是 UTF-8(这是由 Microsoft 的 notepad 等支持的) )。
如果声明了编码,则编码名称必须被 Python 识别。 编码用于所有词法分析,包括字符串文字、注释和标识符。
2.1.5. 显式连接
可以使用反斜杠字符 (\
) 将两个或多个物理行连接成逻辑行,如下所示:当物理行以不属于字符串文字或注释的一部分的反斜杠结尾时,它与在形成单个逻辑行之后,删除反斜杠和后面的行尾字符。 例如:
以反斜杠结尾的行不能带有注释。 反斜杠不会继续评论。 除字符串文字外,反斜杠不会继续标记(即,字符串文字以外的标记不能使用反斜杠跨物理行拆分)。 反斜杠在字符串文字之外的行上的其他地方是非法的。
2.1.6. 隐式连线
圆括号、方括号或花括号中的表达式可以在不使用反斜杠的情况下拆分为多个物理行。 例如:
隐式连续行可以带有注释。 连续行的缩进并不重要。 允许空白续行。 隐式连续行之间没有 NEWLINE 标记。 隐式连续行也可以出现在三引号字符串中(见下文); 在这种情况下,他们不能发表评论。
2.1.7. 空行
仅包含空格、制表符、换页符和可能的注释的逻辑行将被忽略(即,不生成 NEWLINE 标记)。 在语句的交互式输入期间,根据 read-eval-print 循环的实现,对空行的处理可能会有所不同。 在标准的交互式解释器中,一个完全空白的逻辑行(即 一个甚至不包含空格或注释)终止多行语句。
2.1.8. 缩进
逻辑行开头的前导空格(空格和制表符)用于计算该行的缩进级别,进而用于确定语句的分组。
制表符由 1 到 8 个空格替换(从左到右),这样直到并包括替换的字符总数是 8 的倍数(这与 Unix 使用的规则相同)。 第一个非空白字符之前的空格总数决定了该行的缩进。 不能使用反斜杠将缩进拆分为多个物理行; 直到第一个反斜杠的空格决定了缩进。
如果源文件混合制表符和空格的方式使意义依赖于空格中制表符的价值,则缩进将被拒绝为不一致; 在这种情况下会引发 TabError。
跨平台兼容性说明:由于非UNIX平台上的文本编辑器的性质,在单个源文件中使用空格和制表符的混合缩进是不明智的。 还应该注意的是,不同的平台可能会明确限制最大缩进级别。
换页符可能出现在行首; 上面的缩进计算将被忽略。 出现在前导空格其他地方的换页字符具有未定义的效果(例如,它们可能会将空格计数重置为零)。
连续行的缩进级别用于使用堆栈生成 INDENT 和 DEDENT 标记,如下所示。
在读取文件的第一行之前,将一个零压入堆栈; 这将永远不会再次弹出。 压入堆栈的数字将始终从下到上严格增加。 在每个逻辑行的开头,将行的缩进级别与堆栈顶部进行比较。 如果相等,则什么都不会发生。 如果较大,则将其压入堆栈,并生成一个 INDENT 令牌。 如果较小,则 必须 是堆栈中出现的数字之一; 堆栈上所有较大的数字都被弹出,对于每个弹出的数字,都会生成一个 DEDENT 令牌。 在文件末尾,为堆栈中剩余的每个大于零的数字生成一个 DEDENT 令牌。
下面是一段正确(虽然令人困惑)缩进的 Python 代码示例:
以下示例显示了各种缩进错误:
(实际上,解析器检测到前三个错误;词法分析器只发现最后一个错误——return r
的缩进与从堆栈中弹出的级别不匹配。)
2.1.9. 令牌之间的空格
除了在逻辑行的开头或在字符串文字中,空格、制表符和换页符可以互换使用来分隔标记。 仅当它们的串联可以被解释为不同的标记(例如,ab 是一个标记,但 ab 是两个标记)时,两个标记之间才需要空格。
2.2. 其他代币
除了 NEWLINE、INDENT 和 DEDENT 之外,还存在以下类别的标记:identifiers、keywords、literals、operators和分隔符。 空白字符(除了行终止符,前面讨论过)不是标记,而是用于分隔标记。 在存在歧义的情况下,从左到右读取时,标记包含形成合法标记的最长可能字符串。
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 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 类别代码代表:
- Lu - 大写字母
- Ll - 小写字母
- Lt - 大写字母
- Lm - 修饰字母
- Lo - 其他字母
- Nl - 字母数字
- Mn - 非间距标记
- Mc - 间距组合标记
- Nd - 十进制数
- Pc - 连接符标点
- Other_ID_Start - PropList.txt 中的显式字符列表以支持向后兼容
- Other_ID_Continue - 同样
所有标识符在解析时都转换为范式NFKC; 标识符的比较基于 NFKC。
可以在 https://www.unicode.org/Public/13.0.0/ucd/DerivedCoreProperties.txt 找到列出 Unicode 4.1 的所有有效标识符字符的非规范 HTML 文件
2.3.1. 关键词
以下标识符用作保留字,或语言的关键字,不能用作普通标识符。 它们必须完全按照此处所写的拼写:
2.3.2. 保留的标识符类
某些类别的标识符(除了关键字)具有特殊含义。 这些类由前导和尾随下划线字符的模式标识:
_*
不是由
from module import *
导入的。 交互式解释器中使用特殊标识符_
来存储上次评估的结果; 它存储在 builtins 模块中。 不处于交互模式时,_
没有特殊含义,也没有定义。 参见导入语句部分。笔记
名称
_
常与国际化结合使用; 有关此约定的更多信息,请参阅 gettext 模块的文档。__*__
系统定义的名称,非正式地称为“dunder”名称。 这些名称由解释器及其实现(包括标准库)定义。 当前系统名称在 特殊方法名称 部分和其他地方讨论。 在 Python 的未来版本中可能会定义更多。 任何 使用
__*__
名称,在任何上下文中,不遵循明确记录的使用,都会在没有警告的情况下被破坏。__*
类私有名称。 此类别中的名称在类定义的上下文中使用时,会重新编写以使用重整形式,以帮助避免基类和派生类的“私有”属性之间的名称冲突。 请参阅 标识符(名称) 部分。
2.4. 文字
文字是某些内置类型的常量值的符号。
2.4.1. 字符串和字节文字
字符串文字由以下词法定义描述:
stringliteral ::= [stringprefix](shortstring | longstring) stringprefix ::= "r" | "u" | "R" | "U" | "f" | "F" | "fr" | "Fr" | "fR" | "FR" | "rf" | "rF" | "Rf" | "RF" 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 或更大的字节必须用转义符表示。
字符串和字节文字都可以选择以字母 'r'
或 'R'
为前缀; 此类字符串称为 原始字符串 并将反斜杠视为文字字符。 因此,在字符串文字中,未对原始字符串中的 '\U'
和 '\u'
转义进行特殊处理。 鉴于 Python 2.x 的原始 unicode 文字的行为与 Python 3.x 的不同,不支持 'ur'
语法。
3.3 版新功能:原始字节文字的 'rb'
前缀已添加为 'br'
的同义词。
3.3 版中的新功能: 重新引入了对 unicode 传统文字 (u'value'
) 的支持,以简化双 Python 2.x 和 3.x 代码库的维护。 有关更多信息,请参阅 PEP 414。
前缀为 'f'
或 'F'
的字符串文字是 格式的字符串文字 ; 请参阅 格式化字符串文字 。 'f'
可以与 'r'
组合,但不能与 'b'
或 'u'
组合,因此原始格式化字符串是可能的,但格式化字节文字不是。
在三重引号文字中,允许(并保留)未转义的换行符和引号,但连续三个未转义的引号终止文字。 (“引号”是用于打开文字的字符,即 '
或 "
。)
除非存在 '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 位十六进制值的字符 xxxxxxxx | (6) |
笔记:
与标准 C 一样,最多接受三个八进制数字。
与标准 C 不同,需要正好两个十六进制数字。
在字节文字中,十六进制和八进制转义表示具有给定值的字节。 在字符串文字中,这些转义符表示具有给定值的 Unicode 字符。
3.3 版更改: 添加了对别名 1 的支持。
正好需要四个十六进制数字。
任何 Unicode 字符都可以通过这种方式进行编码。 正好需要八个十六进制数字。
与标准 C 不同,所有无法识别的转义序列都保留在字符串中不变,即 反斜杠保留在结果 中。 (此行为在调试时很有用:如果转义序列输入错误,则结果输出更容易被识别为已损坏。)同样重要的是要注意,仅在字符串文字中识别的转义序列属于无法识别的字节转义类别文字。
3.6 版更改: 无法识别的转义序列会产生 DeprecationWarning。 在 Python 的某些未来版本中,它们将是 SyntaxError。
即使在原始文字中,引号也可以用反斜杠转义,但反斜杠仍保留在结果中; 例如,r"\""
是一个有效的字符串文字,由两个字符组成:一个反斜杠和一个双引号; r"\"
不是有效的字符串文字(即使原始字符串也不能以奇数个反斜杠结尾)。 具体来说, 原始文字不能以单个反斜杠 结尾(因为反斜杠会转义以下引号字符)。 另请注意,单个反斜杠后跟换行符被解释为这两个字符作为文字的一部分,而不是 作为行的延续。
2.4.2. 字符串字面量连接
允许多个相邻的字符串或字节文字(由空格分隔),可能使用不同的引用约定,它们的含义与其连接相同。 因此,"hello" 'world'
等价于 "helloworld"
。 此功能可用于减少所需的反斜杠数量,方便地跨长行拆分长字符串,甚至为字符串的某些部分添加注释,例如:
请注意,此功能是在语法级别定义的,但在编译时实现。 '+' 运算符必须用于在运行时连接字符串表达式。 另请注意,文字串联可以为每个组件使用不同的引用样式(甚至混合原始字符串和三重引号字符串),并且格式化字符串文字可以与纯字符串文字串联。
2.4.3. 格式化字符串文字
3.6 版中的新功能。
格式的字符串文字 或 f-string 是以 'f'
或 'F'
为前缀的字符串文字。 这些字符串可能包含替换字段,这些字段是由大括号 {}
分隔的表达式。 虽然其他字符串文字始终具有常量值,但格式化字符串实际上是在运行时计算的表达式。
转义序列像在普通字符串文字中一样解码(除非文字也被标记为原始字符串)。 解码后,字符串内容的语法为:
f_string ::= (literal_char | "{{" | "}}" | replacement_field)* replacement_field ::= "{" f_expression ["!" conversion] [":" format_spec] "}" f_expression ::= (conditional_expression | "*" or_expr) ("," conditional_expression | "," "*" or_expr)* [","] | yield_expression conversion ::= "s" | "r" | "a" format_spec ::= (literal_char | NULL | replacement_field)* literal_char ::= <any code point except "{", "}" or NULL>
除了任何双花括号 '{{'
或 '}}'
被相应的单花括号替换之外,花括号之外的字符串部分按字面处理。 单个左大括号 '{'
标记替换字段,该字段以 Python 表达式开头。 在表达式之后,可能有一个转换字段,由感叹号 '!'
引入。 也可以附加格式说明符,由冒号 ':'
引入。 替换字段以结束大括号 '}'
结束。
格式化字符串文字中的表达式被视为用括号括起来的正则 Python 表达式,但有一些例外。 不允许使用空表达式,并且 lambda 表达式必须用显式括号括起来。 替换表达式可以包含换行符(例如 在三引号字符串中),但它们不能包含注释。 每个表达式都在格式化字符串文字出现的上下文中按从左到右的顺序进行计算。
如果指定了转换,则在格式化之前转换表达式的计算结果。 转换 '!s'
对结果调用 str(),'!r'
调用 repr(),'!a'
调用 ascii ()。
然后使用 format() 协议格式化结果。 格式说明符传递给表达式或转换结果的 __format__()
方法。 省略格式说明符时传递空字符串。 然后将格式化的结果包含在整个字符串的最终值中。
顶级格式说明符可能包括嵌套的替换字段。 这些嵌套字段可能包括它们自己的转换字段和 格式说明符 ,但可能不包括更深层嵌套的替换字段。 格式说明符 mini-language 与字符串 .format() 方法使用的相同。
格式化字符串文字可以连接,但替换字段不能跨文字拆分。
格式化字符串文字的一些示例:
与常规字符串文字共享相同语法的结果是替换字段中的字符不得与外部格式化字符串文字中使用的引用冲突:
格式表达式中不允许使用反斜杠,否则会引发错误:
要包含需要反斜杠转义的值,请创建一个临时变量。
格式化的字符串文字不能用作文档字符串,即使它们不包含表达式。
另请参阅 PEP 498 以了解添加格式化字符串文字的提议,以及使用相关格式字符串机制的 str.format()。
2.4.4. 数字文字
数字字面量分为三种类型:整数、浮点数和虚数。 没有复杂的文字(可以通过将实数和虚数相加来形成复数)。
请注意,数字文字不包含符号; 像 -1
这样的短语实际上是由一元运算符 '-
' 和文字 1
组成的表达式。
2.4.5. 整数文字
整数文字由以下词法定义描述:
integer ::= decinteger | bininteger | octinteger | hexinteger decinteger ::= nonzerodigit (["_"] digit)* | "0"+ (["_"] "0")* bininteger ::= "0" ("b" | "B") (["_"] bindigit)+ octinteger ::= "0" ("o" | "O") (["_"] octdigit)+ hexinteger ::= "0" ("x" | "X") (["_"] hexdigit)+ 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 | exponentfloat pointfloat ::= [digitpart] fraction | digitpart "." exponentfloat ::= (digitpart | pointfloat) exponent digitpart ::= digit (["_"] digit)* fraction ::= "." digitpart exponent ::= ("e" | "E") ["+" | "-"] digitpart
请注意,整数和指数部分始终使用基数 10 进行解释。 例如,077e010
是合法的,表示与 77e10
相同的数字。 浮点文字的允许范围取决于实现。 与整数文字一样,数字分组支持下划线。
浮点文字的一些示例:
3.6 版更改: 现在允许在文字中使用下划线进行分组。
2.4.7. 虚字
虚字面量由以下词法定义描述:
imagnumber ::= (floatnumber | digitpart) ("j" | "J")
虚数产生一个实部为 0.0 的复数。 复数表示为一对浮点数,并且对它们的范围有相同的限制。 要创建具有非零实部的复数,请为其添加一个浮点数,例如 (3+4j)
。 一些虚构文字的例子:
2.5. 运营商
以下令牌是运算符:
+ - * ** / // % @
<< >> & | ^ ~
< > <= >= == !=
2.6. 分隔符
以下标记用作语法中的分隔符:
( ) [ ] { }
, : . ; @ = ->
+= -= *= /= //= %= @=
&= |= ^= >>= <<= **=
句点也可以出现在浮点和虚数文字中。 三个句点的序列作为省略号字面量具有特殊含义。 列表的后半部分,即扩充赋值运算符,在词法上用作分隔符,但也执行操作。
以下打印 ASCII 字符作为其他标记的一部分具有特殊含义,或者对词法分析器具有其他意义:
' " # \
Python 中不使用以下打印 ASCII 字符。 它们出现在字符串文字和注释之外是一个无条件错误:
$ ? `
脚注
2.1.3. 注释
注释以不属于字符串文字的散列字符 (
#
) 开头,并在物理行的末尾结束。 除非调用隐式行连接规则,否则注释表示逻辑行的结束。 语法会忽略注释。