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词符生成)。在交互式输入语句时,空白行的处理可能不同,这取决于read-eval-print循环的实现。在标准的交互式解释器,完全空白的逻辑行 (即一个既不含空格也不包含注释) 终止多行语句。

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 - 大写字母
  • Ll - 小写字母
  • Lt - 首字母大写
  • Lm - 修饰符字母
  • Lo - 其它字母
  • Nl - 字母数字
  • 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字符作为其它词符的一部分有着特殊的含义,或者对于词法分析器具有重要作用:

'       "       #       \

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

$       ?       `

脚注

[1]http://www.unicode.org/Public/8.0.0/ucd/NameAliases.txt