2. Lexical analysis

Python 程序由* parser 读取。解析器的 Importing 是由词法分析器生成的令牌*流。本章介绍词法分析器如何将文件分解为令牌。

Python 将 7 位 ASCII 字符集用于程序文本。

2.3 版中的新Function:编码语句可用于指示字符串 Literals 和 Comments 使用的编码与 ASCII 不同。

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

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

将来的兼容性说明: 可能会很想像一下,假设 8 位字符的字符集是 ISO Latin-1(一个覆盖了大多数使用拉丁字母的西方语言的 ASCII 超集),但是将来的 Unicode 文本编辑器将变得很普遍。这些通常使用 UTF-8 编码,它也是 ASCII 超集,但对于序数为 128-255 的字符使用非常不同。尽管在此主题上尚未达成共识,但假设当前的实施似乎偏向于 Latin-1,但假设采用 Latin-1 或 UTF-8 是不明智的。这适用于源字符集和运行时字符集。

2.1. 行结构

Python 程序分为多个逻辑行

2.1.1. 逻辑行

逻辑行的结尾由令牌 NEWLINE 表示。语句不能跨越逻辑行边界,除非语法允许使用 NEWLINE(例如,复合语句中的语句之间)。pass遵循显式或隐式“线连接”规则,由一条或多条“物理行”构成一条逻辑行。

2.1.2. 物理行

物理行是由行尾序列终止的字符序列。在源文件和字符串中,可以使用任何标准平台的行终止 Sequences-使用 ASCII LF(换行)的 Unix 形式,使用 ASCII 序列 CR LF(返回然后换行)的 Windows 形式,或使用 ASCII 序列的旧 Macintosh 形式。 ASCII CR(返回)字符。无论平台如何,所有这些形式都可以平等使用。Importing 的结尾还充当finally物理行的隐式终止符。

嵌入 Python 时,应使用换行符的标准 C 约定将源代码字符串传递给 Python API(代表 ASCII LF 的\n字符是行终止符)。

2.1.3. Comments

Comments 以不属于字符串 Literals 的哈希字符(#)开头,并在物理行的结尾处结束。Comments 表示逻辑行的结尾,除非调用了隐式行连接规则。语法将忽略 Comments。他们不是令牌。

2.1.4. 编码语句

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

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

GNU Emacs 也认识到这一点,并且

# vim:fileencoding=<encoding-name>

被 Bram Moolenaar 的 VIM 认可。此外,如果文件的第一个字节为 UTF-8 字节 Sequences 标记('\xef\xbb\xbf'),则语句的文件编码为 UTF-8(除其他外,Microsoft 的 notepad 支持)。

如果语句了编码,则编码名称必须由 Python 识别。该编码用于所有词法分析,尤其是查找字符串的结尾并解释 UnicodeLiterals 的内容。字符串 Literals 会转换为 Unicode 进行语法分析,然后在解释开始之前转换回其原始编码。

2.1.5. 显式连接

可以使用反斜杠字符(\)将两个或多个物理行连接为逻辑行,如下所示:当物理行以反斜杠结尾时,该反斜杠不是字符串 Literals 或 Comments 的一部分,则将其与以下行连接起来,形成一个逻辑行,删除反斜杠和以下行尾字符。例如:

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

以反斜杠结尾的行不能带有 Comments。反斜杠不会 continue 发表 Comment。除字符串 Literals 外,反斜杠不会延续令牌(即,字符串 Literals 之外的令牌不能使用反斜杠在物理行之间分割)。反斜杠在字符串 Literals 之外的其他地方是非法的。

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

隐含的连续行可以带有 Comments。连续线的缩进并不重要。允许使用空白连续行。隐式连续行之间没有 NEWLINE 令牌。在三引号字符串中也可能出现隐含的连续行(请参见下文);在这种情况下,他们将无法发表 Comment。

2.1.7. 空行

仅包含空格,制表符,换页符和可能包含 Comments 的逻辑行将被忽略(即,不会生成 NEWLINE 令牌)。在语句的交互式 Importing 期间,根据 read-eval-print 循环的实现,空白行的处理可能有所不同。在标准实现中,一个完全空白的逻辑行(即不包含空格或 Comments 的逻辑行)终止多行语句。

2.1.8. Indentation

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

首先,将制表符替换(从左至右)一到八个空格,以使直到包括该替换在内的字符总数为八个的倍数(这与 Unix 所使用的规则相同)。然后,第一个非空白字符之前的空格总数将确定该行的缩进。缩进不能使用反斜线分割成多条物理行;直到第一个反斜杠的空格都会确定缩进。

跨平台兼容性说明: 由于非 UNIX 平台上的文本编辑器的性质,在单个源文件中将空格和制表符混合用于缩进是不明智的。还应注意,不同平台可能会明确限制最大缩进级别。

行的开头可能会出现换页符;上面的缩进计算将忽略它。前导空格中其他位置出现的换页字符具有不确定的效果(例如,它们可能会将空格计数重置为零)。

连续行的缩进级别用于使用堆栈生成 INDENT 和 DEDENT 令牌,如下所示。

在读取文件的第一行之前,将单个零压入堆栈。这将不再弹出。压入堆栈的数字将始终严格按照从下到上的 Sequences 递增。在每条逻辑行的开头,将行的缩进级别与堆栈顶部进行比较。如果相等,则什么也不会发生。如果较大,则将其压入堆栈,并生成一个 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. 令牌之间的空白

除了逻辑行的开头或字符串 Literals 外,空格字符空格,制表符和换页符可以互换使用以分隔标记。仅当两个令牌的级联可以被解释为其他令牌时(例如,ab 是一个令牌,而 b 是两个令牌),才需要在两个令牌之间使用空格。

2.2. 其他token

除了 NEWLINE,INDENT 和 DEDENT,还存在以下类别的令牌:标识符关键字Literals运算符定界符。空格字符(前面讨论过的行终止符除外)不是标记,而是用来分隔标记。在存在歧义的地方,当从左向右读取时,令牌包括形成合法令牌的最长字符串。

2.3. 标识符和关键字

标识符(也称为* name *)由以下词汇定义描述:

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

标识符的长度是无限的。情况很重要。

2.3.1. Keywords

以下标识符用作保留字或语言的“关键字”,而不能用作普通标识符。它们的拼写必须与此处完全相同:

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 future Function。

在 2.6 版中进行了更改:aswith是完整关键字。

2.3.2. 保留的标识符类别

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

  • _*

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

Note

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

  • __*__

    • 系统定义的名称。这些名称由解释器及其实现(包括标准库)定义。当前的系统名称将在特殊方法名称部分和其他地方讨论。将来的 Python 版本中可能会定义更多内容。 *在任何情况下,任何未使用__*__名称的操作(未遵循明确记录的使用方法)都会遭到破坏,恕不另行通知。
  • __*

    • 类私有名称。当在类定义的上下文中使用时,此类别中的名称将被重写,以使用变形的形式来帮助避免 Base Class 和派生类的“私有”属性之间的名称冲突。参见Identifiers (Names)部分。

2.4. Literals

Literals 是某些内置类型的常量值的表示法。

2.4.1. 字符串 Literals

字符串 Literals 由以下词汇定义描述:

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与字符串 Literals 的其余部分之间不允许有空格。源字符集由编码语句定义。如果源文件中未给出编码语句,则为 ASCII;参见Encoding declarations部分。

用简单的英语:字符串 Literals 可以用匹配的单引号(')或双引号(")括起来。也可以将它们括在匹配的组中,该组具有三个单引号或双引号(通常称为三重引号字符串)。反斜杠(\)字符用于转义具有特殊含义的字符,例如换行符,反斜杠本身或引号字符。字符串 Literals 可以选择以字母'r''R'作为前缀;这样的字符串称为“原始字符串”,并使用不同的规则来解释反斜杠转义序列。前缀'u''U'使字符串成为 Unicode 字符串。 Unicode 字符串使用 Unicodeunion 和 ISO 10646 定义的 Unicode 字符集。Unicode 字符串中提供了一些下面描述的其他转义序列。在 Python 2 中,前缀'b''B'被忽略;表示 Literals 应该在 Python 3 中成为字节 Literals(例如,当代码使用 2to3 自动转换时)。 'u''b'前缀后可以带有'r'前缀。

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

除非存在'r''R'前缀,否则将根据类似于标准 C 所使用的规则来解释字符串中的转义序列。可识别的转义序列为:

Escape SequenceMeaningNotes
\newlineIgnored
\\反斜杠(\)
\'单引号(')
\"双引号(")
\aASCII 铃声(BEL)
\bASCII 退格键(BS)
\fASCII 换页(FF)
\nASCII 换行(LF)
\N{name}Unicode 数据库中名为* name *的字符(仅 Unicode)
\rASCII 回车(CR)
\tASCII 水平制表符(TAB)
\uxxxx具有 16 位十六进制值* xxxx *的字符(仅 Unicode)(1)
\Uxxxxxxxx具有 32 位十六进制值* xxxxxxxx *的字符(仅 Unicode)(2)
\vASCII 垂直制表符(VT)
\ooo具有八进制值的字符* ooo *(3,5)
\xhh具有十六进制值* hh *的字符(4,5)

Notes:

  • 可以使用此转义序列对形成代理对的一部分的各个代码单元进行编码。

  • 任何 Unicode 字符都可以用这种方式编码,但是如果 Python 被编译为使用 16 位代码单元(默认值),则基本多语言平面(BMP)之外的字符将使用代理对进行编码。

  • 与标准 C 一样,最多可以接受三个八进制数字。

  • 与标准 C 中不同,仅需要两个十六进制数字。

  • 在字符串 Literals 中,十六进制和八进制转义表示具有给定值的字节。字节不必在源字符集中编码一个字符。在 UnicodeLiterals 中,这些转义符表示具有给定值的 Unicode 字符。

与标准 C 不同,所有无法识别的转义序列均保留在字符串中,即,反斜杠保留在字符串中。 (此行为在调试时很有用:如果转义序列 Importing 错误,则更容易将输出识别为break.)还必须注意,上表中标记为“(仅 Unicode)”的转义序列属于非 Unicode 字符串 Literals 的无法识别的转义的类别。

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

'r''R'前缀与'u''U'前缀结合使用时,将处理\uXXXX\UXXXXXXXX转义序列,而所有其他反斜杠都留在字符串中。例如,字符串 Literalsur"\u0062\n"由三个 Unicode 字符组成:“拉丁文小写字母 B”,“ REVERSE SOLIDUS”和“拉丁文小写字母 N”。反斜杠可以使用前面的反斜杠进行转义;但是,两者都保留在字符串中。结果,仅当反斜杠数量奇数时,才能识别\uXXXX转义序列。

2.4.2. 字符串 Literals 串联

允许使用可能使用不同的引用约定的多个相邻字符串 Literals(由空格分隔),它们的含义与它们的串联相同。因此,"hello" 'world'等效于"helloworld"。此Function可用于减少所需的反斜杠数量,在长行之间方便地分割长字符串,甚至可为字符串的部分添加 Comments,例如:

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

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

2.4.3. 数值 Literals

数字 Literals 有四种类型:纯整数,长整数,浮点数和虚数。没有复杂的 Literals(可以pass添加实数和虚数来形成复数)。

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

2.4.4. 整数和长整数 Literals

以下词汇定义描述了整数和长整数 Literals:

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)的平原整数 Literals 将被接受,就好像它们是长整数一样。 [1]除了可以存储在可用内存中的内容外,长整数 Literals 没有限制。

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

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

2.4.5. 浮点 Literals

浮点 Literals 由以下词汇定义描述:

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

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

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

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

2.4.6. 虚构 Literals

虚构的 Literals 由以下词汇定义描述:

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

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

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

2.5. Operators

以下标记是运算符:

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

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

2.6. Delimiters

以下标记用作语法中的定界符:

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

该句点也可以出现在浮点数和虚数 Literals 中。三个周期的序列具有特殊的含义,即片中的Ellipsis号。列表的后半部分,扩展的赋值运算符,在词法上充当定界符,但也执行操作。

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

'       "       #       \

以下印刷 ASCII 字符未在 Python 中使用。它们出现在字符串 Literals 和 Comments 之外是一个无条件错误:

$       ?

Footnotes

  • [1]
    • 在 2.4 之前的 Python 版本中,八进制和十六进制 Literals 的范围正好在最大的可表示纯整数之上,但在最大的无符号 32 位数字之下(在使用 32 位算术的机器上)4294967296,被视为负整数。从无符号值减去 4294967296 所得的整数。