python基础教程之词法分析

python基础教程之词法分析


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. 缩进

逻辑行开始的前导空白(空格和制表符)用来计算行的缩进层级,然后用它决定语句的分组。

首先,每个制表符被替换(从左到右)成八个空格,这样包括替换后的字符的总数是八的整数(这是为了和Unix 使用一样的规则)。 非空白字符之前的空格总数决定该行的缩进。 缩进不可以使用反斜杠分割成多个物理行;直到第一个反斜杠处的空白决定了缩进。

Indentation is rejected as inconsistent if a source file mixes tabs and spaces in a way that makes the meaning dependent on the worth of a tab in spaces; 在这种情况下将引发一个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

在U+0001 至U+007F 范围之内,标识符的合法字符与Python 2.x 相同:大写和小写的AZ、下划线_ 和不是首字母的数字09

Python 3.0 引入这个范围之外的新的字符(参见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 – modifier letters
  • Lo – other letters
  • Nl – 字母数字
  • Mn – nonspacing marks
  • Mc – spacing combining marks
  • Nd – decimal numbers
  • Pc – connector punctuations
  • Other_ID_Start – explicit list of characters in PropList.txt to support backwards
    compatibility
  • Other_ID_Continue – likewise

All identifiers are converted into the normal form NFKC while parsing; comparison of identifiers is based on NFKC.

A non-normative HTML file listing all valid identifier characters for Unicode 4.1 can be found at http://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. String and Bytes literals

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

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; 参考编码声明一节。

In plain English: Both types of literals can be enclosed in matching single quotes (') or double quotes ("). 它们也可以包含在配对的三个单引号或双引号组中(这些字符串一般称为三引号字符串)。The backslash (\) character is used to escape characters that otherwise have a special meaning, such as newline, backslash itself, or the quote character.

字节常量使用'b'或者'B'前缀;they produce an instance of the bytes type instead of the str type. They may only contain ASCII characters; bytes with a numeric value of 128 or greater must be expressed with escapes.

As of Python 3.3 it is possible again to prefix string literals with a u prefix to simplify maintenance of dual 2.x and 3.x codebases.

Both string and bytes literals may optionally be prefixed with a letter 'r' or 'R'; such strings are called raw strings and treat backslashes as literal characters. As a result, in string literals, '\U' and '\u' escapes in raw strings are not treated specially. Given that Python 2.x’s raw unicode literals behave differently than Python 3.x’s the 'ur' syntax is not supported.

New in version 3.3: The 'rb' prefix of raw bytes literals has been added as a synonym of 'br'.

New in version 3.3: Support for the unicode legacy literal (u'value') was reintroduced to simplify the maintenance of dual Python 2.x and 3.x codebases. See PEP 414 for more information.

In triple-quoted literals, unescaped newlines and quotes are allowed (and are retained), except that three unescaped quotes in a row terminate the literal. (A “quote” is the character used to open the literal, i.e. either ' or ".)

Unless an 'r' or 'R' prefix is present, escape sequences in string and bytes literals are interpreted according to rules similar to those used by Standard C. 可识别的转义序列有:

转义序列 含义 说明
\newline Backslash and newline ignored  
\\ 反斜杠 (\)  
\' 单引号 (')  
\" 双引号 (")  
\a ASCII Bell (BEL)  
\b ASCII退格 (BS)  
\f ASCII跳页
(FF)
 
\n ASCII换行 (LF)  
\r ASCII Carriage Return (CR)  
\t ASCII 水平分隔符 (TAB)  
\v ASCII 垂直分隔符 (VT)  
\ooo 八进制值为ooo的字符 (1,3)
\xhh 十六进制值为hh的字符 (2,3)

Escape sequences only recognized in string literals are:

转义序列 含义 说明
\N{name} Character named name in the Unicode database (4)
\uxxxx Character with 16-bit hex value xxxx (5)
\Uxxxxxxxx Character with 32-bit hex value xxxxxxxx (6)

说明:

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

  2. 与标准C 不同,要求两个精确的十六进制数字。

  3. In a bytes literal, hexadecimal and octal escapes denote the byte with the given value. In a string literal, these escapes denote a Unicode character with the given value.

  4. Changed in version 3.3: Support for name aliases [1] has been added.

  5. 形成代理对一部分的代码单元可以使用这种转义序列编码。Exactly four hex digits are required.

  6. Any Unicode character can be encoded this way. Exactly eight hex digits are required.

Unlike Standard C, all unrecognized escape sequences are left in the string unchanged, i.e., the backslash is left in the result. (这个行为在调试的时候特别有用:如果敲错一个转义序列,输出的结果可以很容易看出是有问题的。)It is also important to note that the escape sequences only recognized in string literals fall into the category of unrecognized escapes for bytes literals.

Even in a raw literal, quotes can be escaped with a backslash, but the backslash remains in the result; for example, r"\"" is a valid string literal consisting of two characters: a backslash and a double quote; r"\" is not a valid string literal (even a raw string cannot end in an odd number of backslashes). Specifically, a raw literal cannot end in a single backslash (since the backslash would escape the following quote character). Note also that a single backslash followed by a newline is interpreted as those two characters as part of the literal, not as a line continuation.


2.4.2. 字符串字面值的连接

Multiple adjacent string or bytes literals (delimited by whitespace), possibly using different quoting conventions, are allowed, and their meaning is the same as their concatenation. Thus, "hello" 'world' is equivalent to "helloworld". 这个特性能够用于减少需要的反斜杠的数目以方便地把很长的字符串分成几行,或者甚至给字符串的某些部分加上注释,例如:

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

注意,这个特性在语法层面上定义,但是在编译的时候实现。The ‘+’ operator must be used to concatenate string expressions at run time. 还需要注意,字面值的连接可以为每个部分使用不同的引号风格(即使是混合原始字符串和三引号字符串)。


2.4.3. 数值字面值

There are three types of numeric literals: integers, floating point numbers, and imaginary numbers. 没有复数字面值(复数字面值可以由一个实数和一个虚数相加形成)。

Note that numeric literals do not include a sign; a phrase like -1 is actually an expression composed of the unary operator ‘-‘ and the literal 1.


2.4.4. Integer literals

Integer literals are described by the following lexical definitions:

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"

There is no limit for the length of integer literals apart from what can be stored in available memory.

Note that leading zeros in a non-zero decimal number are not allowed. This is for disambiguation with C-style octal literals, which Python used before version 3.0.

Some examples of integer literals:

7     2147483647                        0o177    0b100110111
3     79228162514264337593543950336     0o377    0x100000000
      79228162514264337593543950336              0xdeadbeef

2.4.5. 浮点数字面值

浮点数字面值由以下词法定义描述:

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

Note that the integer and exponent parts are always interpreted using radix 10. 例如,077e010是合法的,它和77e10表示同一个数。浮点数字面值允许的范围依赖于具体的实现。一些浮点数字面值的例子:

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

Note that numeric literals do not include a sign; a phrase like -1 is actually an expression composed of the unary operator - and the literal 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/6.3.0/ucd/NameAliases.txt

留下回复