Skip to main content

2. 词汇分析

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

Python将程序文本读为Unicode代码点;源文件的编码可以由编码声明给出,默认为UTF-8,有关详细信息,请参阅 PEP 3120。如果源文件不能被解码,则产生 SyntaxError

2.1. 线结构

一个Python程序分为多个 逻辑线

2.1.1. 逻辑线

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

2.1.2. 物理线

物理行是由行尾序列终止的字符序列。在源文件中,可以使用任何标准平台行终止序列 - 使用ASCII LF(换行)的Unix格式,使用ASCII序列CR LF(返回后跟换行)的Windows格式,或使用ASCII的旧Macintosh格式CR(返回)字符。所有这些形式可以平等地使用,不管平台。

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

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字节顺序标记(b'\xef\xbb\xbf'),声明的文件编码是UTF-8(这是由Microsoft的 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令牌。如果它较小,则 must 是堆栈中出现的数字之一;堆栈上更大的所有数字被弹出,并且为每个弹出的数字生成一个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是一个令牌,而b是两个令牌)时,两个令牌之间需要空白。

2.2. 其他令牌

除了NEWLINE,INDENT和DEDENT之外,存在以下类别的令牌:identifierskeywordsliteralsoperatorsdelimiters。空白字符(除了行终止符,前面讨论过的)不是令牌,而是用于定界令牌。当存在歧义时,当从左向右读时,令牌包括形成合法令牌的最长可能的字符串。

2.3. 标识符和关键字

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

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。

列出Unicode 4.1的所有有效标识符字符的非规范HTML文件可以在 https://www.dcl.hpi.uni-potsdam.de/home/loewis/table-3131.html 找到。

2.3.1. 关键词

以下标识符用作保留字,或语言的 keywords,不能用作普通标识符。他们必须拼写完全如这里所写:

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版本中定义。 Any 使用 __*__ 名称,在任何情况下,不遵循明确记录的使用,受到破坏,没有警告。

__*

类私有名称。此类别中的名称在类定义的上下文中使用时,将重写为使用损坏的形式,以帮助避免基类和派生类的“私有”属性之间的名称冲突。参见 标识符(名称)

2.4. 文字

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

2.4.1. 字符串和字节字面值

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

stringliteral   ::=  [stringprefix](shortstring | longstring)
stringprefix    ::=  "r" | "u" | "R" | "U" | "f" | "F"
                     | "fr" | "Fr" | "fR" | "FR" | "rf" | "rF" | "Rf" | "RF"
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 strings,并将反斜杠作为文字字符。因此,在字符串字面量中,不对原始字符串中的 '\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

在其前缀中具有 'f''F' 的字符串字面量是 formatted string literal;见 格式化字符串文字'f' 可以与 'r' 组合,但不与 'b''u' 组合,因此原始格式化字符串是可能的,但格式化字节字面不是。

在三引号文字中,允许未转义的换行符和引号(并保留),除了一行中的三个未转义引号终止文字。 (“报价”是用于打开文字的字符,即 '"。)

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

转义序列

含义

笔记

\newline

忽略反斜杠和换行符

 

\\

反斜杠(\

 

\'

单引号('

 

\"

双引号("

 

\a

ASCII贝尔(BEL)

 

\b

ASCII退格(BS)

 

\f

ASCII进给(FF)

 

\n

ASCII换行(LF)

 

\r

ASCII回车(CR)

 

\t

ASCII水平制表符(TAB)

 

\v

ASCII垂直制表符(VT)

 

\ooo

八进制值的字符 ooo

(1,3)

\xhh

十六进制值 hh 的字符

(2,3)

仅在字符串文字中识别的转义序列是:

转义序列

含义

笔记

\N{name}

Unicode数据库中名为 name 的字符

(4)

\uxxxx

16位十六进制值的字符 xxxx

(5)

\Uxxxxxxxx

具有32位十六进制值 xxxxxxxx 的字符

(6)

笔记:

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

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

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

  4. 在 3.3 版更改: 支持名称别名已添加 [1]

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

  6. 任何Unicode字符都可以用这种方式编码。正确的八个十六进制数字是必需的。

与标准C不同,所有不可识别的转义序列保持不变,即 反斜杠留在结果中。 (这种行为在调试时非常有用:如果转义序列错误,生成的输出更容易被识别为破坏)。还需要注意的是,仅在字符串文字中识别的转义序列属于字节的无法识别转义的类别文字。

在 3.6 版更改: 无法识别的转义序列生成DeprecationWarning。在Python的一些未来版本中,它们将是一个SyntaxError。

即使在原始文本中,引号也可以用反斜杠转义,但反斜杠保留在结果中;例如,r"\"" 是一个有效的字符串文字,由两个字符组成:反斜杠和双引号; r"\" 不是有效的字符串文字(即使原始字符串不能以奇数个反斜杠结尾)。具体来说,原始文本不能以单个反斜杠结尾 (因为反斜杠将转义以下引号字符)。还要注意,单个反斜杠后跟换行解释为这两个字符作为文字的一部分,not 作为行连续。

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. 格式化字符串文字

3.6 新版功能.

formatted string literalf-string 是以 'f''F' 为前缀的字符串文字。这些字符串可以包含替换字段,其是由花括号 {} 分隔的表达式。尽管其他字符串常量总是具有常量值,但是格式化的字符串实际上是在运行时求值的表达式。

转义序列像普通字符串文字一样被解码(除非文字也被标记为原始字符串)。解码后,字符串内容的语法为:

f_string          ::=  (literal_char | "{{" | "}}" | replacement_field)*
replacement_field ::=  "{" f_expression ["!" conversion] [":" format_spec] "}"
f_expression      ::=  (conditional_expression | "*" or_expr)
                         ("," conditional_expression | "," "*" or_expr)* [","]
                       | yield_expression
conversion        ::=  "s" | "r" | "a"
format_spec       ::=  (literal_char | NULL | replacement_field)*
literal_char      ::=  <any code point except "{", "}" or NULL>

除了任何双倍的大括号 '{{''}}' 被相应的单个大括号替换之外,大括号外的大括号的部分被逐字地处理。单个打开的大括号 '{' 标记替换字段,该字段以Python表达式开头。在表达式之后,可以存在由感叹号 '!' 引入的转换字段。还可以附加格式说明符,由冒号 ':' 引入。替换字段以结束的花括号 '}' 结束。

格式化字符串字面量的表达式被视为被括号括起来的常规Python表达式,有一些例外。不允许使用空表达式,并且 lambda 表达式必须由显式括号包围。替换表达式可以包含换行符(例如,在三引号字符串中),但它们不能包含注释。每个表达式在格式化的字符串文字的上下文中按照从左到右的顺序进行计算。

如果指定了转换,则在格式化之前转换计算表达式的结果。转换 '!s' 对结果调用 str()'!r' 调用 repr()'!a' 调用 ascii()

然后使用 format() 协议格式化结果。格式说明符被传递到表达式或转换结果的 __format__() 方法。当省略格式说明符时,将传递空字符串。格式化的结果然后被包括在整个字符串的最终值中。

顶级格式说明符可以包括嵌套替换字段。这些嵌套字段可以包括它们自己的转换字段和格式说明符,但可以不包括更深层嵌套的替换字段。

格式化的字符串文字可以连接,但替换字段不能跨文本拆分。

格式化字符串文字的一些示例:

>>> name = "Fred"
>>> f"He said his name is {name!r}."
"He said his name is 'Fred'."
>>> f"He said his name is {repr(name)}."  # repr() is equivalent to !r
"He said his name is 'Fred'."
>>> width = 10
>>> precision = 4
>>> value = decimal.Decimal("12.34567")
>>> f"result: {value:{width}.{precision}}"  # nested fields
'result:      12.35'

与常规字符串文字共享相同语法的结果是替换字段中的字符不得与外部格式化字符串文字中使用的引用冲突:

f"abc {a["x"]} def"    # error: outer string literal ended prematurely
f"abc {a['x']} def"    # workaround: use different quoting

格式表达式中不允许使用反斜杠,并且会引发错误:

f"newline: {ord('\n')}"  # raises SyntaxError

要包括需要反斜杠转义的值,请创建一个临时变量。

>>> newline = ord('\n')
>>> f"newline: {newline}"
'newline: 10'

有关添加格式化字符串文字的提议以及使用相关格式字符串机制的 str.format(),请参阅 PEP 498

2.4.4. 数字文字

有三种类型的数字文字:整数,浮点数和虚数。没有复杂的文字(复数可以通过添加一个实数和一个虚数形成)。

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

2.4.5. 整数文字

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

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

除了可以存储在可用内存中的整数文字的长度没有限制。

下划线被忽略以确定文字的数值。它们可用于将数字分组以增强可读性。一个下划线可以出现在数字之间,并且在基本说明符后面,如 0x

请注意,不允许非零十进制数的前导零。这是为了消除与C风格的八进制文字,这是Python 3.0之前使用。

整数文本的一些示例:

7     2147483647                        0o177    0b100110111
3     79228162514264337593543950336     0o377    0xdeadbeef
      100_000_000_000                   0b_1110_0101

在 3.6 版更改: 下划线现在允许在文字中进行分组。

2.4.6. 浮点文字

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

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

注意,整数和指数部分总是使用基数10来解释。例如,077e010 是合法的,并且表示与 77e10 相同的数字。浮点文字的允许范围是与实现相关的。在整数文字中,数字分组支持下划线。

浮点文本的一些示例:

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

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

在 3.6 版更改: 下划线现在允许在文字中进行分组。

2.4.7. 虚构文字

虚构文本由以下词法定义描述:

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

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

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

2.5. 操作员

以下标记是运算符:

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

2.6. 分隔符

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

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

周期也可以出现在浮点和虚数字面上。三个周期的序列具有作为省略文字的特殊含义。列表的后半部分,增强的赋值运算符,在词法上作为分隔符,但也执行操作。

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

'       "       #       \

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

$       ?       `

脚注

[1]

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