2. 词法分析

Python 程序由一个*解析器*读取。 解析器的输入是由*词法分析器*(也称为 *tokenizer* 或*分词器*)生成的 词法单元 流。 本章介绍词法分析器如何生成这些词法单元。

词法分析器会确定程序文本的 编码 (默认为 UTF-8),并将文本解码为 源字符。 如果文本无法解码,则会引发 SyntaxError

接下来,词法分析器使用源字符来生成词法单元流。所生成词法单元的类型通常取决于下一个要处理的源字符。类似地,分析器的其他特殊行为也取决于尚未处理的第一个源字符。下表快速总结了这些源字符,并提供了指向包含更多信息章节的链接。

字符

下一个词法单元(或其他相关文档)

  • 空格

  • 制表符

  • 换页符

  • CR, LF

  • 反斜杠 (\)

  • 井号 (#)

  • 引号 (', ")

  • ASCII 字母 (a-z, A-Z)

  • 非 ASCII 字符

  • 下划线 (_)

  • 数字 (0-9)

  • 点 (.)

  • 问号 (?)

  • 美元符号 ($)

  • 反引号 (`)

  • 控制字符

  • 错误(在字符串字面值和注释之外)

  • 其他可打印字符

  • 文件结束

2.1. 行结构

一个 Python 程序会分为多个*逻辑行*。

2.1.1. 逻辑行

逻辑行的结束由词法单元 NEWLINE 表示。 语句不能跨越逻辑行边界,除非语法允许 NEWLINE (例如,在复合语句中的语句之间)。 一个逻辑行由一个或多个 *物理行* 遵循显式隐式的*行拼接*规则构成。

2.1.2. 物理行

物理行是以以下行尾序列之一结束的字符序列

  • Unix 形式,使用 ASCII LF (换行符),

  • Windows 形式,使用 ASCII 序列 CR LF (回车后跟换行符),

  • 经典 Mac OS”形式,使用 ASCII CR (回车符)。

无论平台如何,这些序列中的每一个都会被替换为单个 ASCII LF (换行符)。(即使在字符串字面值内部也是如此。)每行可以使用任何一种序列;它们在文件中不必保持一致。

输入结束也作为最后一个物理行的隐式终止符。

形式上

newline: <ASCII LF> | <ASCII CR> <ASCII LF> | <ASCII CR>

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,则开头的 UTF-8 字节顺序标记 (b'\xef\xbb\xbf') 会被忽略而不是导致语法错误。

如果声明了编码,则编码名称必须被 Python 识别(参见标准编码)。该编码将用于所有词法分析,包括字符串字面值、注释和标识符。

所有词法分析,包括字符串字面值、注释和标识符,都在使用源编码解码后的 Unicode 文本上进行。除了 NUL 控制字符外,任何 Unicode 码点都可以出现在 Python 源码中。

source_character:  <any Unicode code point, except NUL>

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

逻辑行开头的行首空白(空格和制表符)用于计算该行的缩进级别,这又被用来确定语句的分组。

制表符会被(从左到右)替换为一到八个空格,以使替换后包括替换在内的总字符数是八的倍数(这旨在与 Unix 使用的规则相同)。然后,第一个非空白字符之前的空格总数决定了该行的缩进。缩进不能使用反斜杠跨越多个物理行;第一个反斜杠之前的空白决定了缩进。

如果源文件混合使用制表符和空格的方式使得其含义依赖于一个制表符等于多少个空格,则缩进将被视为不一致而被拒绝;在这种情况下会引发 TabError

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

换页符可以出现在行首;在上述缩进计算中它将被忽略。出现在行首空白其他位置的换页符具有未定义的效果(例如,它们可能会将空格计数重置为零)。

连续行的缩进级别用于生成 INDENTDEDENT 词法单元,使用一个栈,如下所示。

在读取文件的第一行之前,一个零被压入栈中;这个零将永远不会被弹出。压入栈中的数字将始终从底到顶严格递增。在每个逻辑行的开头,该行的缩进级别将与栈顶进行比较。如果相等,则什么也不做。如果更大,则将其压入栈中,并生成一个 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 是两个词法单元。然而,+a+ a 都产生两个词法单元,+a,因为 +a 不是一个有效的词法单元。

2.1.10. 结束标记

在非交互式输入的末尾,词法分析器会生成一个 ENDMARKER 词法单元。

2.2. 其他词法单元

除了 NEWLINEINDENTDEDENT 之外,还存在以下几类词法单元:*标识符*和*关键字* (NAME),*字面值*(例如 NUMBERSTRING),以及其他符号(*运算符*和*分隔符*,OP)。空白字符(除了前面讨论过的逻辑行终止符)不是词法单元,但用于分隔词法单元。当存在歧义时,一个词法单元由从左到右读取时能构成合法词法单元的最长字符串组成。

2.3. 名称(标识符和关键字)

NAME 词法单元代表*标识符*、*关键字*和*软关键字*。

在 ASCII 范围(U+0001..U+007F)内,名称的有效字符包括大写和小写字母(A-Za-z)、下划线 _,以及除了首字符外的数字 09

名称必须至少包含一个字符,但没有长度上限。大小写是敏感的。

除了 A-Za-z_0-9 之外,名称还可以使用 ASCII 范围之外的“类字母”和“类数字”字符,详见下文。

所有标识符在解析时都会被转换为 规范化形式 NFKC;标识符的比较基于 NFKC。

形式上,规范化标识符的首字符必须属于集合 id_start,它是以下各项的并集:

  • Unicode 类别 <Lu> - 大写字母(包括 AZ

  • Unicode 类别 <Ll> - 小写字母(包括 az

  • Unicode 类别 <Lt> - 词首大写字母

  • Unicode 类别 <Lm> - 修饰字母

  • Unicode 类别 <Lo> - 其他字母

  • Unicode 类别 <Nl> - 字母数字

  • {"_"} - 下划线

  • <Other_ID_Start> - 在 PropList.txt 中为了支持向后兼容而明确指定的字符集

其余字符必须属于集合 id_continue,它是以下各项的并集:

  • id_start 中的所有字符

  • Unicode 类别 <Nd> - 十进制数字(包括 09

  • Unicode 类别 <Pc> - 连接标点

  • Unicode 类别 <Mn> - 非间距标记

  • Unicode 类别 <Mc> - 间距组合标记

  • <Other_ID_Continue> - 在 PropList.txt 中为了支持向后兼容而明确指定的另一个字符集

Unicode 类别使用 unicodedata 模块中包含的 Unicode 字符数据库版本。

这些集合基于 Unicode 标准附件 UAX-31。更多详情请参阅 PEP 3131

更形式化地,名称由以下词法定义描述:

NAME:         xid_start xid_continue*
id_start:     <Lu> | <Ll> | <Lt> | <Lm> | <Lo> | <Nl> | "_" | <Other_ID_Start>
id_continue:  id_start | <Nd> | <Pc> | <Mn> | <Mc> | <Other_ID_Continue>
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*)">
identifier:   <NAME, except keywords>

Unicode 字符数据库中的 DerivedCoreProperties.txt 文件提供了由 Unicode 定义的所有有效标识符字符的非规范性列表。

2.3.1. 关键字

以下名称被用作语言的保留字,或称*关键字*,不能用作普通标识符。它们必须完全按照此处所示的拼写使用:

False      await      else       import     pass
None       break      except     in         raise
True       class      finally    is         return
and        continue   for        lambda     try
as         def        from       nonlocal   while
assert     del        global     not        with
async      elif       if         or         yield

2.3.2. 软关键字

在 3.10 版本加入。

有些名称仅在特定上下文中被保留。这些被称为*软关键字*:

  • 当在 match 语句中使用时,matchcase_

  • 当在 type 语句中使用时,type

这些在它们的特定上下文中语法上表现为关键字,但这种区分是在解析器层面进行的,而不是在分词时。

作为软关键字,它们在语法中的使用是可能的,同时仍然保持与使用这些名称作为标识符的现有代码的兼容性。

在 3.12 版本发生变更: type 现在是一个软关键字。

2.3.3. 保留的标识符类

某些类别的标识符(除了关键字)具有特殊含义。这些类别通过前导和尾随下划线字符的模式来识别:

_*

不会被 from module import * 导入。

_

match 语句中的 case 模式里,_ 是一个软关键字,表示一个通配符

另外,交互式解释器将上一次求值的结果放在变量 _ 中。(它与内置函数如 print 一起存储在 builtins 模块中。)

在其他地方,_ 是一个普通标识符。它通常用于命名“特殊”项,但对 Python 本身来说并不特殊。

备注

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

它也常用于未使用的变量。

__*__

系统定义的名称,非正式地称为“dunder”名称。这些名称由解释器及其实现(包括标准库)定义。当前的系统名称在特殊方法名称部分和其他地方讨论。未来版本的 Python 可能会定义更多。*任何*在任何上下文中对 __*__ 名称的使用,如果不遵循明确文档化的用法,都可能在没有警告的情况下被破坏。

__*

类私有名称。当在类定义上下文中使用时,此类别中的名称会被重写以使用一种变形形式,以帮助避免基类和派生类的“私有”属性之间的名称冲突。请参见标识符(名称)部分。

2.4. 字面值

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

在词法分析方面,Python 有字符串、字节串数字字面值。

其他“字面值”在词法上使用关键字NoneTrueFalse)和特殊的省略号词法单元...)来表示。

2.5. 字符串和字节串字面值

字符串字面值是用单引号 (') 或双引号 (") 括起来的文本。例如:

"spam"
'eggs'

用于开始字面值的引号也用于结束它,因此字符串字面值只能包含另一种引号(除非使用转义序列,见下文)。例如:

'Say "Hello", please.'
"Don't do that!"

除了这个限制,引号字符('")的选择不会影响字面值的解析方式。

在字符串字面值内部,反斜杠(\)字符引入一个*转义序列*,它根据反斜杠后的字符具有特殊含义。例如,\" 表示双引号字符,并且*不*会结束字符串:

>>> print("Say \"Hello\" to everyone!")
Say "Hello" to everyone!

有关此类序列的完整列表和更多详细信息,请参见下文的转义序列

2.5.1. 三引号字符串

字符串也可以用匹配的三组单引号或双引号括起来。这些通常被称为*三引号字符串*:

"""This is a triple-quoted string."""

在三引号字面值中,允许使用未转义的引号(并且会保留),但连续三个未转义的引号会终止字面值,如果它们与开始时使用的引号类型相同('"):

"""This string has "quotes" inside."""

未转义的换行符也是允许的并会被保留:

'''This triple-quoted string
continues on the next line.'''

2.5.2. 字符串前缀

字符串字面值可以有一个可选的*前缀*,它会影响字面值内容的解析方式,例如:

b"data"
f'{result=}'

允许的前缀有:

有关每种类型的详细信息,请参阅链接的部分。

前缀不区分大小写(例如,‘B’ 的作用与 ‘b’ 相同)。‘r’ 前缀可以与 ‘f’、‘t’ 或 ‘b’ 组合,因此 ‘fr’、‘rf’、‘tr’、‘rt’、‘br’ 和 ‘rb’ 也是有效的前缀。

在 3.3 版本加入: 添加了原始字节串字面值的 'rb' 前缀作为 'br' 的同义词。

为了简化维护 Python 2.x 和 3.x 双代码库,重新引入了对 unicode 遗留字面值(u'value')的支持。更多信息请参阅 PEP 414

2.5.3. 形式语法

除了“f-字符串”“t-字符串”,字符串字面值由以下词法定义描述。

这些定义使用负向先行断言!)来表示结束引号会终止字面值。

STRING:          [stringprefix] (stringcontent)
stringprefix:    <("r" | "u" | "b" | "br" | "rb"), case-insensitive>
stringcontent:
   | "'''" ( !"'''" longstringitem)* "'''"
   | '"""' ( !'"""' longstringitem)* '"""'
   | "'" ( !"'" stringitem)* "'"
   | '"' ( !'"' stringitem)* '"'
stringitem:      stringchar | stringescapeseq
stringchar:      <any source_character, except backslash and newline>
longstringitem:  stringitem | newline
stringescapeseq: "\" <any source_character>

请注意,与所有词法定义一样,空白是重要的。特别是,前缀(如果有)必须紧跟在起始引号之后。

2.5.4. 转义序列

除非存在 ‘r’ 或 ‘R’ 前缀,否则字符串和字节串字面值中的转义序列将根据与标准 C 类似的规则进行解释。可识别的转义序列是:

转义序列

含义

\<换行>

忽略的行尾

\\

反斜杠

\'

单引号

\"

双引号

\a

ASCII 响铃 (BEL)

\b

ASCII 退格 (BS)

\f

ASCII 换页 (FF)

\n

ASCII 换行 (LF)

\r

ASCII 回车 (CR)

\t

ASCII 水平制表符 (TAB)

\v

ASCII 垂直制表符 (VT)

\ooo

八进制字符

\xhh

十六进制字符

\N{name}

具名 Unicode 字符

\uxxxx

十六进制 Unicode 字符

\Uxxxxxxxx

十六进制 Unicode 字符

2.5.4.1. 被忽略的行尾

可以在行尾添加反斜杠以忽略换行符:

>>> 'This string will not include \
... backslashes or newline characters.'
'This string will not include backslashes or newline characters.'

可以使用三引号字符串或圆括号和字符串字面值拼接来达到同样的效果。

2.5.4.2. 转义字符

要在非原始 Python 字符串字面值中包含反斜杠,必须将其加倍。 \\ 转义序列表示单个反斜杠字符:

>>> print('C:\\Program Files')
C:\Program Files

类似地,\'\" 序列分别表示单引号和双引号字符:

>>> print('\' and \"')
' and "

2.5.4.3. 八进制字符

序列 \ooo 表示一个八进制(基数为 8)值为 ooo 的*字符*:

>>> '\120'
'P'

最多接受三位八进制数字(0 到 7)。

在字节串字面值中,*字符*意味着一个具有给定值的*字节*。在字符串字面值中,它意味着一个具有给定值的 Unicode 字符。

在 3.11 版本发生变更: 值大于 0o377 (255) 的八进制转义会产生一个 DeprecationWarning

在 3.12 版本发生变更: 值大于 0o377 (255) 的八进制转义会产生一个 SyntaxWarning。在未来的 Python 版本中,它们将引发一个 SyntaxError

2.5.4.4. 十六进制字符

序列 \xhh 表示一个十六进制(基数为 16)值为 hh 的*字符*:

>>> '\x50'
'P'

与标准 C 不同,这里要求必须是两个十六进制数字。

在字节串字面值中,*字符*意味着一个具有给定值的*字节*。在字符串字面值中,它意味着一个具有给定值的 Unicode 字符。

2.5.4.5. 具名 Unicode 字符

序列 \N{name} 表示一个具有给定*名称*的 Unicode 字符:

>>> '\N{LATIN CAPITAL LETTER P}'
'P'
>>> '\N{SNAKE}'
'🐍'

此序列不能出现在字节串字面值中。

在 3.3 版本发生变更: 已添加对名称别名的支持。

2.5.4.6. 十六进制 Unicode 字符

序列 \uxxxx\Uxxxxxxxx 表示具有给定十六进制(基数为 16)值的 Unicode 字符。\u 要求正好四个数字;\U 要求正好八个数字。后者可以编码任何 Unicode 字符。

>>> '\u1234'
'ሴ'
>>> '\U0001f40d'
'🐍'

这些序列不能出现在字节串字面值中。

2.5.4.7. 未识别的转义序列

与标准 C 不同,所有未识别的转义序列都会在字符串中保持不变,也就是说,*反斜杠会保留在结果中*:

>>> print('\q')
\q
>>> list('\q')
['\\', 'q']

请注意,对于字节串字面值,仅在字符串字面值中识别的转义序列(\N...\u...\U...)属于未识别的转义类别。

在 3.6 版本发生变更: 未识别的转义序列会产生一个 DeprecationWarning

在 3.12 版本发生变更: 未识别的转义序列会产生一个 SyntaxWarning。在未来的 Python 版本中,它们将引发一个 SyntaxError

2.5.5. 字节串字面值

*字节串字面值*总是以 ‘b’ 或 ‘B’ 为前缀;它们产生 bytes 类型的实例,而不是 str 类型。它们只能包含 ASCII 字符;数值为 128 或更大的字节必须用转义序列表示(通常是十六进制字符八进制字符):

>>> b'\x89PNG\r\n\x1a\n'
b'\x89PNG\r\n\x1a\n'
>>> list(b'\x89PNG\r\n\x1a\n')
[137, 80, 78, 71, 13, 10, 26, 10]

类似地,零字节必须使用转义序列(通常是 \0\x00)来表示。

2.5.6. 原始字符串字面值

字符串和字节串字面值都可以选择性地以字母 ‘r’ 或 ‘R’ 为前缀;这样的构造分别称为*原始字符串字面值*和*原始字节串字面值*,它们将反斜杠视为字面字符。因此,在原始字符串字面值中,转义序列不会被特殊处理:

>>> r'\d{4}-\d{2}-\d{2}'
'\\d{4}-\\d{2}-\\d{2}'

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

2.5.7. f-字符串

在 3.6 版本加入。

一个*格式化字符串字面值*或 *f-字符串* 是以 ‘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 | replacement_field)*
literal_char:      <any code point except "{", "}" or NULL>

字符串中花括号之外的部分按字面处理,但任何双花括号 '{{''}}' 都会被替换为相应的单个花括号。单个左花括号 '{' 标记一个替换字段,它以一个 Python 表达式开始。为了同时显示表达式文本及其求值后的值(在调试时很有用),可以在表达式后添加一个等号 '='。后面可以跟一个由感叹号 '!' 引入的转换字段。还可以附加一个由冒号 ':' 引入的格式说明符。替换字段以一个右花括号 '}' 结束。

格式化字符串字面值中的表达式被视为由圆括号包围的常规 Python 表达式,但有几个例外。不允许空表达式,lambda 和赋值表达式 := 必须用显式圆括号包围。每个表达式都在格式化字符串字面值出现的上下文中从左到右求值。替换表达式可以在单引号和三引号 f-字符串中包含换行符,并且它们可以包含注释。在替换字段中 # 之后的所有内容都是注释(即使是右括号和引号)。在这种情况下,替换字段必须在不同的行中关闭。

>>> f"abc{a # This is a comment }"
... + 3}"
'abc5'

在 3.7 版本发生变更: 在 Python 3.7 之前,由于实现问题,格式化字符串字面值的表达式中不允许使用 await 表达式和包含 async for 子句的推导式。

在 3.12 版本发生变更: 在 Python 3.12 之前,f-字符串替换字段内不允许有注释。

当提供等号 '=' 时,输出将包含表达式文本、'=' 和求值后的值。左花括号 '{' 之后、表达式内部以及 '=' 之后的所有空格都会保留在输出中。默认情况下,'=' 会提供表达式的 repr(),除非指定了格式。当指定了格式时,它默认为表达式的 str(),除非声明了 '!r' 转换。

在 3.8 版本加入: 等号 '='

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

然后使用 format() 协议对结果进行格式化。格式说明符会传递给表达式或转换结果的 __format__() 方法。如果省略格式说明符,则传递一个空字符串。格式化的结果随后被包含在整个字符串的最终值中。

顶层格式说明符可以包含嵌套的替换字段。这些嵌套字段可以包含它们自己的转换字段和格式说明符,但不能包含更深层嵌套的替换字段。格式说明符迷你语言str.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'
>>> today = datetime(year=2017, month=1, day=27)
>>> f"{today:%B %d, %Y}"  # using date format specifier
'January 27, 2017'
>>> f"{today=:%B %d, %Y}" # using date format specifier and debugging
'today=January 27, 2017'
>>> number = 1024
>>> f"{number:#0x}"  # using integer format specifier
'0x400'
>>> foo = "bar"
>>> f"{ foo = }" # preserves whitespace
" foo = 'bar'"
>>> line = "The mill's closed"
>>> f"{line = }"
'line = "The mill\'s closed"'
>>> f"{line = :20}"
"line = The mill's closed   "
>>> f"{line = !r:20}"
'line = "The mill\'s closed" '

在替换字段内重用外部 f-字符串的引号类型是允许的:

>>> a = dict(x=2)
>>> f"abc {a["x"]} def"
'abc 2 def'

在 3.12 版本发生变更: 在 Python 3.12 之前,在替换字段内重用外部 f-字符串的相同引号类型是不可能的。

反斜杠也允许在替换字段中使用,并且其求值方式与在任何其他上下文中相同:

>>> a = ["a", "b", "c"]
>>> print(f"List a contains:\n{"\n".join(a)}")
List a contains:
a
b
c

在 3.12 版本发生变更: 在 Python 3.12 之前,f-字符串替换字段内不允许有反斜杠。

格式化字符串字面值不能用作文档字符串,即使它们不包含表达式。

>>> def foo():
...     f"Not a docstring"
...
>>> foo.__doc__ is None
True

另请参阅 PEP 498(添加了格式化字符串字面值的提案),以及 str.format(),它使用了一种相关的格式化字符串机制。

2.5.8. t-字符串

在 3.14 版本加入。

一个*模板字符串字面值*或*t-字符串*是以 ‘t’ 或 ‘T’ 为前缀的字符串字面值。这些字符串遵循与格式化字符串字面值相同的语法和求值规则,但有以下区别:

  • 模板字符串字面值求值后得到一个 string.templatelib.Template 对象,而不是一个 str 对象。

  • 不使用 format() 协议。相反,格式说明符和转换(如果有)会传递给为每个求值表达式创建的一个新的 Interpolation 对象。由处理生成的 Template 对象的代码来决定如何处理格式说明符和转换。

  • 包含嵌套替换字段的格式说明符会先被急切求值,然后再传递给 Interpolation 对象。例如,一个形式为 {amount:.{precision}f} 的插值会先求值内部表达式 {precision} 以确定 format_spec 属性的值。如果 precision2,那么生成的格式说明符将是 '.2f'

  • 当在插值表达式中提供等号 '=' 时,表达式的文本会附加到相关插值之前的字面字符串中。这包括等号和任何周围的空白。表达式的 Interpolation 实例将正常创建,只是 conversion 将默认设置为 ‘r’ (repr())。如果提供了显式的转换或格式说明符,则会覆盖默认行为。

2.6. 数字字面值

NUMBER 词法单元代表数字字面值,共有三种类型:整数、浮点数和虚数。

NUMBER: integer | floatnumber | imagnumber

数字字面值的数值与其作为字符串传递给 intfloatcomplex 类构造函数的数值相同。请注意,并非所有这些构造函数的有效输入也都是有效的字面值。

数字字面值不包括符号;像 -1 这样的短语实际上是由一元运算符 ‘-’ 和字面值 1 组成的表达式。

2.6.1. 整数字面值

整数字面值表示整数。例如:

7
3
2147483647

整数字面值的长度没有限制,除了可用内存所能存储的范围:

7922816251426433759354395033679228162514264337593543950336

下划线可用于对数字进行分组以增强可读性,并且在确定字面值的数值时会被忽略。例如,以下字面值是等效的:

100_000_000_000
100000000000
1_00_00_00_00_000

下划线只能出现在数字之间。例如,_123321_123__321 都*不是*有效的字面值。

整数可以分别使用前缀 0b0o0x 指定为二进制(基数 2)、八进制(基数 8)或十六进制(基数 16)。十六进制数字 10 到 15 由字母 A-F 表示,不区分大小写。例如:

0b100110111
0b_1110_0101
0o177
0o377
0xdeadbeef
0xDead_Beef

下划线可以跟在进制说明符之后。例如,0x_1f 是一个有效的字面值,但 0_x1f0x__1f 不是。

非零十进制数中不允许有前导零。例如,0123 不是一个有效的字面值。这是为了与 C 风格的八进制字面值进行区分,Python 在 3.0 版本之前使用这种风格。

形式上,整数字面值由以下词法定义描述:

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

在 3.6 版本发生变更: 现在允许在字面值中使用下划线进行分组。

2.6.2. 浮点数字面值

浮点(float)字面值,如 3.141.5,表示实数的近似值

它们由*整数*部分和*小数*部分组成,每个部分都由十进制数字构成。这两个部分由小数点 . 分隔。

2.71828
4.0

与整数字面值不同,允许有前导零。例如,077.010 是合法的,并且表示与 77.01 相同的数字。

与整数字面值一样,单个下划线可以出现在数字之间以帮助提高可读性:

96_485.332_123
3.14_15_93

这两个部分中的任何一个都可以为空,但不能同时为空。例如:

10.  # (equivalent to 10.0)
.001  # (equivalent to 0.001)

可选地,整数和分数部分后面可以跟一个*指数*:字母 eE,后跟一个可选的符号 +-,以及一个与整数和分数部分格式相同的数字。eE 表示“乘以十的……次方”:

1.0e3  # (represents 1.0×10³, or 1000.0)
1.166e-5  # (represents 1.166×10⁻⁵, or 0.00001166)
6.02214076e+23  # (represents 6.02214076×10²³, or 602214076000000000000000.)

在只有整数和指数部分的浮点数中,小数点可以省略:

1e3  # (equivalent to 1.e3 and 1.0e3)
0e0  # (equivalent to 0.)

形式上,浮点字面值由以下词法定义描述:

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

在 3.6 版本发生变更: 现在允许在字面值中使用下划线进行分组。

2.6.3. 虚数字面值

Python 有复数对象,但没有复数字面值。相反,*虚数字面值*表示实部为零的复数。

例如,在数学中,复数 3+4.2i 写为实数 3 加上虚数 4.2i。Python 使用类似的语法,只是虚数单位写为 j 而不是 i

3+4.2j

这是一个由整数字面值 3运算符+’ 和虚数字面值 4.2j 组成的表达式。由于这是三个独立的词法单元,它们之间允许有空格:

3 + 4.2j

每个词法单元*内部*不允许有空格。特别是,j 后缀不能与它前面的数字分开。

j 前面的数字与浮点数字面值具有相同的语法。因此,以下是有效的虚数字面值:

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

与浮点数字面值不同,如果虚数只有整数部分,小数点可以省略。该数字仍然被评估为浮点数,而不是整数:

10j
0j
1000000000000000000000000j   # equivalent to 1e+24j

j 后缀不区分大小写。这意味着你可以使用 J 来代替:

3.14J   # equivalent to 3.14j

形式上,虚数字面值由以下词法定义描述:

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

2.7. 运算符和分隔符

以下语法定义了*运算符*和*分隔符*词法单元,即通用的 OP 词法单元类型。在 token 模块文档中也提供了这些词法单元及其名称的列表

OP:
   | assignment_operator
   | bitwise_operator
   | comparison_operator
   | enclosing_delimiter
   | other_delimiter
   | arithmetic_operator
   | "..."
   | other_op

assignment_operator:   "+=" | "-=" | "*=" | "**=" | "/="  | "//=" | "%=" |
                       "&=" | "|=" | "^=" | "<<=" | ">>=" | "@="  | ":="
bitwise_operator:      "&"  | "|"  | "^"  | "~"   | "<<"  | ">>"
comparison_operator:   "<=" | ">=" | "<"  | ">"   | "=="  | "!="
enclosing_delimiter:   "("  | ")"  | "["  | "]"   | "{"   | "}"
other_delimiter:       ","  | ":"  | "!"  | ";"   | "="   | "->"
arithmetic_operator:   "+"  | "-"  | "**" | "*"   | "//"  | "/"   | "%"
other_op:              "."  | "@"

备注

通常,*运算符*用于组合表达式,而*分隔符*用于其他目的。然而,这两个类别之间没有明确、正式的区别。

一些词法单元可以根据用法既作为运算符又作为分隔符。例如,* 既是乘法运算符,也是用于序列解包的分隔符;@ 既是矩阵乘法运算符,也是引入装饰器的分隔符。

对于某些词法单元,区别并不清楚。例如,有些人认为 .() 是分隔符,而另一些人则将其视为 getattr() 运算符和函数调用运算符。

Python 的一些运算符,如 andornot in,使用关键字词法单元而不是“符号”(运算符词法单元)。

三个连续句点(...)的序列作为 Ellipsis 字面值具有特殊含义。