2. 词法分析 — Python 文档

来自菜鸟教程
Python/docs/2.7/reference/lexical analysis
跳转至:导航、​搜索

2. 词法分析

Python 程序由 解析器 读取。 解析器的输入是 令牌流 ,由 词法分析器 生成。 本章描述词法分析器如何将文件分解为标记。

Python 对程序文本使用 7 位 ASCII 字符集。

2.3 版新功能: 编码声明可用于指示字符串文字和注释使用不同于 ASCII 的编码。


为了与旧版本兼容,Python 仅在找到 8 位字符时才发出警告; 如果这些字节是二进制数据而不是字符,则应通过声明显式编码或使用转义序列来纠正这些警告。

运行时字符集取决于连接到程序的 I/O 设备,但通常是 ASCII 的超集。

未来兼容性注意事项: 假设 8 位字符的字符集是 ISO Latin-1(一个 ASCII 超集,涵盖大多数使用拉丁字母的西方语言)可能很容易,但也有可能未来 Unicode 文本编辑器将变得普遍。 这些通常使用 UTF-8 编码,它也是一个 ASCII 超集,但对于序数为 128-255 的字符的用途非常不同。 虽然在这个问题上还没有达成共识,但假设 Latin-1 或 UTF-8 是不明智的,即使当前的实现似乎支持 Latin-1。 这适用于源字符集和运行时字符集。

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.3. 注释

注释以不属于字符串文字的散列字符 (#) 开头,并在物理行的末尾结束。 除非调用隐式行连接规则,否则注释表示逻辑行的结束。 注释被语法忽略; 它们不是令牌。


2.1.4. 编码声明

如果 Python 脚本的第一行或第二行中的注释与正则表达式 coding[=:]\s*([-\w.]+) 匹配,则将该注释作为编码声明处理; 此表达式的第一组命名源代码文件的编码。 编码声明必须单独出现一行。 如果是第二行,则第一行也必须是仅注释行。 编码表达式的推荐形式是

# -*- coding: <encoding-name> -*-

这也被 GNU Emacs 认可,以及

# vim:fileencoding=<encoding-name>

这被 Bram Moolenaar 的 VIM 认可。 此外,如果文件的第一个字节是 UTF-8 字节顺序标记 ('\xef\xbb\xbf'),则声明的文件编码是 UTF-8(这是由 Microsoft 的 notepad 等支持的) )。

如果声明了编码,则编码名称必须被 Python 识别。 编码用于所有词法分析,特别是查找字符串的结尾以及解释 Unicode 文字的内容。 字符串文字被转换为 Unicode 以进行语法分析,然后在解释开始之前转换回其原始编码。


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 标记)。 在语句的交互式输入期间,根据 read-eval-print 循环的实现,对空行的处理可能会有所不同。 在标准实现中,一个完全空白的逻辑行(即 一个甚至不包含空格或注释)终止多行语句。


2.1.8. 缩进

逻辑行开头的前导空格(空格和制表符)用于计算该行的缩进级别,进而用于确定语句的分组。

首先,制表符被一到八个空格替换(从左到右),这样包括替换在内的字符总数是八的倍数(这与 Unix 使用的规则相同)。 第一个非空白字符之前的空格总数决定了该行的缩进。 不能使用反斜杠将缩进拆分为多个物理行; 直到第一个反斜杠的空格决定了缩进。

跨平台兼容性说明:由于非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 是一个标记,但 ab 是两个标记)时,两个标记之间才需要空格。


2.2. 其他代币

除了 NEWLINE、INDENT 和 DEDENT 之外,还存在以下类别的标记:identifierskeywordsliteralsoperators分隔符。 空白字符(除了行终止符,前面讨论过)不是标记,而是用于分隔标记。 在存在歧义的情况下,从左到右读取时,标记包含形成合法标记的最长可能字符串。


2.3. 标识符和关键字

标识符(也称为 names)由以下词法定义描述:

identifier ::=  (letter|"_") (letter | digit | "_")*
letter     ::=  lowercase | uppercase
lowercase  ::=  "a"..."z"
uppercase  ::=  "A"..."Z"
digit      ::=  "0"..."9"

标识符的长度没有限制。 案例很重要。

2.3.1. 关键词

以下标识符用作保留字,或语言的关键字,不能用作普通标识符。 它们必须完全按照此处所写的拼写:

and       del       from      not       while
as        elif      global    or        with
assert    else      if        pass      yield
break     except    import    print
class     exec      in        raise
continue  finally   is        return
def       for       lambda    try

在 2.4 版更改:None 成为一个常量,现在被编译器识别为内置对象 None 的名称。 尽管它不是关键字,但您不能为其分配不同的对象。


2.5 版更改: 使用 aswith 作为标识符会触发警告。 要将它们用作关键字,请启用 with_statement 未来功能。


2.6 版变更:aswith 为全关键字。


2.3.2. 保留的标识符类

某些类别的标识符(除了关键字)具有特殊含义。 这些类由前导和尾随下划线字符的模式标识:

_*

不是由 from module import * 导入的。 交互式解释器中使用特殊标识符 _ 来存储上次评估的结果; 它存储在 __builtin__ 模块中。 不处于交互模式时,_ 没有特殊含义,也没有定义。 参见导入语句部分。

笔记

名称 _ 常与国际化结合使用; 有关此约定的更多信息,请参阅 gettext 模块的文档。

__*__

系统定义的名称。 这些名称由解释器及其实现(包括标准库)定义。 当前系统名称在 特殊方法名称 部分和其他地方讨论。 在 Python 的未来版本中可能会定义更多。 任何 使用 __*__ 名称,在任何上下文中,不遵循明确记录的使用,都会在没有警告的情况下被破坏。

__*

类私有名称。 此类别中的名称在类定义的上下文中使用时,会重新编写以使用重整形式,以帮助避免基类和派生类的“私有”属性之间的名称冲突。 请参阅 标识符(名称) 部分。


2.4. 文字

文字是某些内置类型的常量值的符号。

2.4.1. 字符串文字

字符串文字由以下词法定义描述:

stringliteral   ::=  [stringprefix](shortstring | longstring)
stringprefix    ::=  "r" | "u" | "ur" | "R" | "U" | "UR" | "Ur" | "uR"
                     | "b" | "B" | "br" | "Br" | "bR" | "BR"
shortstring     ::=  "'" shortstringitem* "'" | '"' shortstringitem* '"'
longstring      ::=  "'''" longstringitem* "'''"
                     | '"""' longstringitem* '"""'
shortstringitem ::=  shortstringchar | escapeseq
longstringitem  ::=  longstringchar | escapeseq
shortstringchar ::=  <any source character except "\" or newline or the quote>
longstringchar  ::=  <any source character except "\">
escapeseq       ::=  "\" <any ASCII character>

这些产生式没有指出的一个语法限制是 stringprefix 和字符串文字的其余部分之间不允许有空格。 源字符集由编码声明定义; 如果源文件中没有给出编码声明,则为 ASCII; 请参阅部分 编码声明

简单的英语:字符串文字可以用匹配的单引号 (') 或双引号 (") 括起来。 它们也可以包含在由三个单引号或双引号组成的匹配组中(这些通常称为 三引号字符串 )。 反斜杠 (\) 字符用于转义具有特殊含义的字符,例如换行符、反斜杠本身或引号字符。 字符串文字可以选择以字母 'r''R' 为前缀; 此类字符串称为 原始字符串 并使用不同的规则来解释反斜杠转义序列。 'u''U' 前缀使字符串成为 Unicode 字符串。 Unicode 字符串使用由 Unicode Consortium 和 ISO 10646 定义的 Unicode 字符集。 一些额外的转义序列,如下所述,在 Unicode 字符串中可用。 'b''B' 的前缀在 Python 2 中被忽略; 它表明文字应该成为 Python 3 中的字节文字(例如 当代码用 2to3 自动转换时)。 'u''b' 前缀可以跟在 'r' 前缀之后。

在三重引号字符串中,允许(并保留)未转义的换行符和引号,但连续三个未转义的引号终止字符串。 (“引号”是用于打开字符串的字符,即 '"。)

除非存在 'r''R' 前缀,字符串中的转义序列将根据与标准 C 使用的规则类似的规则进行解释。 公认的转义序列是:

转义序列 意义 笔记
\newline 忽略
\\ 反斜杠 (\)
\' 单引号 (')
\" 双引号 (")
\a ASCII 钟 (BEL)
\b ASCII 退格 (BS)
\f ASCII 换页 (FF)
\n ASCII 换行 (LF)
\N{name} Unicode 数据库中名为 name 的字符(仅限 Unicode)
\r ASCII 回车 (CR)
\t ASCII 水平制表符 (TAB)
\uxxxx 具有 16 位十六进制值 xxxx 的字符(仅限 Unicode) (1)
\Uxxxxxxxx 具有 32 位十六进制值的字符 xxxxxxxx(仅限 Unicode) (2)
\v ASCII 垂直制表符 (VT)
\ooo 八进制字符 ooo (3,5)
\xhh 具有十六进制值 hh 的字符 (4,5)

注意事项:

  1. 可以使用此转义序列对构成代理对部分的各个代码单元进行编码。
  2. 任何 Unicode 字符都可以用这种方式编码,但如果 Python 被编译为使用 16 位代码单元(默认值),则基本多语言平面 (BMP) 之外的字符将使用代理对进行编码。
  3. 在标准 C 中,最多接受三个八进制数字。
  4. 与标准 C 不同,需要正好两个十六进制数字。
  5. 在字符串文字中,十六进制和八进制转义表示具有给定值的字节; 字节不必对源字符集中的字符进行编码。 在 Unicode 文字中,这些转义符表示具有给定值的 Unicode 字符。

与标准 C 不同,所有无法识别的转义序列都保留在字符串中不变,即 反斜杠保留在字符串 中。 (此行为在调试时很有用:如果转义序列输入错误,则结果输出更容易被识别为已损坏。)同样重要的是要注意上表中标记为“(仅限 Unicode)”的转义序列属于非 Unicode 字符串文字的无法识别的转义类别。

当存在 'r''R' 前缀时,反斜杠后面的字符将包含在字符串中而不更改,并且 所有反斜杠都保留在字符串 中。 例如,字符串文字 r"\n" 由两个字符组成:一个反斜杠和一个小写 'n'。 字符串引号可以用反斜杠转义,但反斜杠保留在字符串中; 例如,r"\"" 是一个由两个字符组成的有效字符串文字:一个反斜杠和一个双引号; r"\" 不是有效的字符串文字(即使原始字符串也不能以奇数个反斜杠结尾)。 具体来说, 原始字符串不能以单个反斜杠 结尾(因为反斜杠会转义以下引号字符)。 另请注意,单个反斜杠后跟换行符被解释为这两个字符作为字符串的一部分, 而非 作为行的延续。

'r''R' 前缀与 'u''U' 前缀结合使用时,则 \uXXXX 和 [ X126X] 转义序列被处理,而 所有其他反斜杠都留在字符串 中。 例如,字符串文字 ur"\u0062\n" 由三个 Unicode 字符组成:“LATIN SMALL LETTER B”、“REVERSE SOLIDUS”和“LATIN SMALL LETTER N”。 反斜杠可以用前面的反斜杠转义; 但是,两者都保留在字符串中。 因此,\uXXXX 转义序列仅在存在奇数个反斜杠时才能识别。


2.4.2. 字符串字面量连接

允许多个相邻的字符串文字(由空格分隔),可能使用不同的引用约定,它们的含义与其连接相同。 因此,"hello" 'world' 等价于 "helloworld"。 此功能可用于减少所需的反斜杠数量,方便地跨长行拆分长字符串,甚至为字符串的某些部分添加注释,例如:

re.compile("[A-Za-z_]"       # letter or underscore
           "[A-Za-z0-9_]*"   # letter, digit or underscore
          )

请注意,此功能是在语法级别定义的,但在编译时实现。 '+' 运算符必须用于在运行时连接字符串表达式。 另请注意,文字连接可以为每个组件使用不同的引用样式(甚至混合原始字符串和三重引用字符串)。


2.4.3. 数字文字

有四种类型的数字文字:普通整数、长整数、浮点数和虚数。 没有复杂的文字(可以通过将实数和虚数相加来形成复数)。

请注意,数字文字不包含符号; 像 -1 这样的短语实际上是由一元运算符 '-' 和文字 1 组成的表达式。


2.4.4. 整数和长整数文字

整数和长整数文字由以下词法定义描述:

longinteger    ::=  integer ("l" | "L")
integer        ::=  decimalinteger | octinteger | hexinteger | bininteger
decimalinteger ::=  nonzerodigit digit* | "0"
octinteger     ::=  "0" ("o" | "O") octdigit+ | "0" octdigit+
hexinteger     ::=  "0" ("x" | "X") hexdigit+
bininteger     ::=  "0" ("b" | "B") bindigit+
nonzerodigit   ::=  "1"..."9"
octdigit       ::=  "0"..."7"
bindigit       ::=  "0" | "1"
hexdigit       ::=  digit | "a"..."f" | "A"..."F"

尽管小写 'l' 和大写 'L' 都允许作为长整数的后缀,但强烈建议始终使用 'L',因为字母 'l'看起来太像数字 '1'

高于最大可表示纯整数(例如,使用 32 位算术时为 2147483647)的纯整数文字被接受,就好像它们是长整数一样。 1 除了可以存储在可用内存中的内容之外,对长整数文字没有限制。

一些普通整数文字(第一行)和长整数文字(第二和第三行)的示例:

7     2147483647                        0177
3L    79228162514264337593543950336L    0377L   0x100000000L
      79228162514264337593543950336             0xdeadbeef

2.4.5. 浮点文字

浮点文字由以下词法定义描述:

floatnumber   ::=  pointfloat | exponentfloat
pointfloat    ::=  [intpart] fraction | intpart "."
exponentfloat ::=  (intpart | pointfloat) exponent
intpart       ::=  digit+
fraction      ::=  "." digit+
exponent      ::=  ("e" | "E") ["+" | "-"] digit+

请注意,浮点数的整数和指数部分可能看起来像八进制整数,但使用基数 10 进行解释。 例如,077e010 是合法的,表示与 77e10 相同的数字。 浮点文字的允许范围取决于实现。 浮点文字的一些示例:

3.14    10.    .001    1e100    3.14e-10    0e0

请注意,数字文字不包含符号; 像 -1 这样的短语实际上是由一元运算符 - 和文字 1 组成的表达式。


2.4.6. 虚字

虚字面量由以下词法定义描述:

imagnumber ::=  (floatnumber | intpart) ("j" | "J")

虚数产生一个实部为 0.0 的复数。 复数表示为一对浮点数,并且对它们的范围有相同的限制。 要创建具有非零实部的复数,请为其添加一个浮点数,例如 (3+4j)。 一些虚构文字的例子:

3.14j   10.j    10j     .001j   1e100j  3.14e-10j

2.5. 运营商

以下令牌是运算符:

+       -       *       **      /       //      %
<<      >>      &       |       ^       ~
<       >       <=      >=      ==      !=      <>

比较运算符 <>!= 是同一运算符的替代拼写。 != 是首选拼写; <> 已过时。


2.6. 分隔符

以下标记用作语法中的分隔符:

(       )       [       ]       {       }      @
,       :       .       `       =       ;
+=      -=      *=      /=      //=     %=
&=      |=      ^=      >>=     <<=     **=

句点也可以出现在浮点和虚数文字中。 三个句点的序列作为切片中的省略号具有特殊含义。 列表的后半部分,即扩充赋值运算符,在词法上用作分隔符,但也执行操作。

以下打印 ASCII 字符作为其他标记的一部分具有特殊含义,或者对词法分析器具有其他意义:

'       "       #       \

Python 中不使用以下打印 ASCII 字符。 它们出现在字符串文字和注释之外是一个无条件错误:

$       ?

脚注

1
在 Python 2.4 之前的版本中,八进制和十六进制文字的范围刚好高于最大可表示的纯整数但低于最大的无符号 32 位数字(在使用 32 位算术的机器上),4294967296,被视为负纯整数通过从它们的无符号值中减去 4294967296 获得的整数。