2. 词法分析

Python程序由解析器读取。输入到解析器中的是由词法分析器生成的符号流。本章讲述词法分析器如何把一个文件拆分成符号。

Python以Unicode码点的方式读取程序文本;源文件的编码可由编码声明给出,否则默认为UTF-8,详细信息参见PEP 3120如果不能解码源文件,则引发 SyntaxError异常。

2.1. 行结构

Python程序分为多个逻辑行

2.1.1. 逻辑行

逻辑行的结束由 NEWLINE 词符表示。除非语法允许 NEWLINE(例如,复合语句的语句之间),否则语句不能跨越逻辑行的边界。通过遵循显式或隐式行连接规则,从一个或多个物理行构造逻辑行。

2.1.2. 物理行

物理行是由行尾序列终止的字符序列。在源文件中,任何标准平台的行终止序列都可以使用 - Unix方式使用 ASCII 的 LF(换行),Windows 方式使用 ASCII 序列 CR LF(回车和换行),旧的 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。除此之外,如果文件开始几个字节是UTF-8的字节顺序标记('\xef\xbb\xbf'),声明的文件编码将是UTF-8(这个特性也被微软的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. 缩进

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

制表符(从左到右)被替换一到八个空格,使得替换的字符总数是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一样:大写和小写字母AZ,下划线_,和非第一个字符的数字09

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 - 大写字母
  • L1 - 小写字母
  • Lt - titlecase letters
  • Lm - 修饰符字母
  • Lo - 其它字母
  • N1 - 字母数字
  • Mn - 非空格标记
  • Mc - 间隔组合标记
  • Nd - 十进制数字
  • Pc - 连接器标点符号
  • Other_ID_Start - PropList.txt中的字符显式列表,以支持向后兼容性
  • Other_ID_Continue - 同样

解析的时候,所有的标识符都转换成正常的NFKC形式;标识符的比较基于NFKC。

有一个不是很规范的HTML文件列出Unicode 4.1的所有有效标识符的字符,它们可以在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模块。不在交互式模式时,_没有特别的含义且是未定义的。请参看import 语句一节。

注意

名字 _ 常用于国际化; 关于本公约的详细信息的文档请参阅 gettext 模块。

__*__
系统定义的名称。这些名称由解释器及其实现(包括标准库)定义。特别方法名称 部分中和其他地方讨论了当前的系统名称。更多可能会在未来的Python版本中定义。任何 __ * __ 名称的 使用,在任何情况下,不遵循明确记录的使用,是易受破损不发出警告。
__*
类私有名称。此类别中的名称在类定义的上下文中使用时,将重写为使用损坏的形式,以帮助避免基本类和派生类的“私有”属性之间的名称冲突。参见标识符(名称)一节。

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>

上面产生式中没有表示出来一个的语法限制是, 在stringprefixbytesprefix与其余字符串字面值之间不允许出现空白字符。字符集由编码声明定义;如果源文件中没有指定编码声明,则为UTF-8;参见编码声明一节。

用简单的中文来描述就是:两种类型的字面值都可以包含在配对的单引号(')或双引号(")中。它们也可以包含在三个单引号或双引号(这些通常称为三引号字符串)的匹配组中。使用反斜杠 (\) 对字符进行转义,如换行符、 反斜杠本身或引号字符的字符进行转义。

字节字面值总是以'b''B'为前缀;它们产生bytes类型而不是str类型的实例。它们只能包含ASCII字符;数值大于等于128的字节必须用转义形式表示。

截至 Python 3.3 很可能再到前缀字符串用 u 前缀来简化维护双 2.x 和 3.x 基本代码。

字符串和字节的字面值都可以用字母'r''R'作为前缀;这样的字符串称为raw string并将反斜杠作为原义字符。因此,在字符串中,'\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 的详细信息。

在三引号字符串中,没有转义的换行和引号是允许的(并且会被保留),除非三个未转义的引号终止了字符串。(引号指用于开始字符串的字符,例如'"。)

除非出现前缀'r''R',否则字符串和字节字面量中的转义序列依照类似标准C使用的规则解释。可识别的转义序列有:

转义序列含义笔记
\newline忽略反斜杠和换行
\\反斜杠(\)
\'单引号(')
\"双引号(")
\aASCII响铃(BEL)
\bASCII退格(BS)
\fASCII换页(FF)
\nASCII换行(LF)
\rASCII回车(CR)
\tASCII水平制表(TAB)
\vASCII垂直制表(VT)
\ooo八进制值ooo的字符(1,3)
\xhh十六进制值hh的字符(2,3)

只在字符串字面量中识别的转义序列是︰

转义序列含义笔记
\N{name}Unicode数据库中名为name的字符(4)
\uxxxx16位的十六进制值为xxxx的字符(5)
\Uxxxxxxxx32位的十六进制值为xxxxxxxx的字符(6)

注:

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

  2. 与标准C不同,只需要两个十六进制数字。

  3. 在字节字面值中,十六进制和八进制转义表示给定值的字节。在字符串字面值中,这些转义表示给定的值的Unicode字符。

  4. 版本 3.3 中的更改︰名称别名 [1] 支持已被添加。

  5. 四个十六进制数字是必需的。

  6. 任何 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
          )

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

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"

整数字面值大小没有限制,除了可用内存的容量。

注意,非0的十进制数前面不允许有0。这和C风格的八进制字面值混淆,Python 3.0之前的版本曾用过。

整数字面值的一些示例:

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+

注意,整数与指数部分总是使用基数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]http://www.unicode.org/Public/8.0.0/ucd/NameAliases.txt