2.语法分析

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

Python将程序文本读作Unicode代码点; 源文件的编码可以通过编码声明给出,默认为UTF-8,参见PEP 3120 了解详情。如果无法解码源文件,SyntaxError则会引发a。

 

2.1。线结构

Python程序分为许多逻辑行

 

2.1.1。逻辑行

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

 

2.1.2。物理线

物理线是由行尾序列终止的字符序列。在源文件和字符串中,可以使用任何标准平台线路终止序列 – 使用ASCII LF(换行)的Unix表单,使用ASCII序列CR LF的Windows表单(返回后跟换行)或使用旧的Macintosh表单ASCII CR(返回)字符。无论平台如何,所有这些形式都可以平等使用。输入的结尾也充当最终物理线的隐式终止符。

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

 

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的记事本支持此功能)。

如果声明了编码,则必须由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使用的规则相同)。然后,第一个非空白字符前面的空格总数确定行的缩进。缩进不能使用反斜杠在多个物理行上分割; 直到第一个反斜杠的空格确定缩进。

如果源文件以一种使得含义依赖于空格中制表符的价值的方式混合制表符和空格,则缩进被拒绝为不一致; TabError在这种情况下,a 被提出。

跨平台兼容性说明:由于非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

 

(实际上,解析器检测到前三个错误;词法分析器只找到最后一个错误 – 缩进与堆栈弹出的级别不匹配。)returnr

 

2.1.9。令牌之间的空格

除了在逻辑行的开头或字符串文字中,空白字符空格,制表符和换页符可以互换使用以分隔标记。仅当两个令牌的串联可以被解释为不同的令牌(例如,ab是一个令牌,但ab是两个令牌)时,才需要两个令牌之间的空格。

 

2.2。其他令牌

除NEWLINE,INDENT和DEDENT外,还存在以下类别的标记: 标识符关键字文字运算符分隔符。空格字符(前面讨论过的行终止符除外)不是标记,而是用于分隔标记。在存在歧义的情况下,当从左到右阅读时,令牌包括形成合法令牌的最长字符串。

 

2.3。标识符和关键字

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

Python中标识符的语法基于Unicode标准附件UAX-31,详细说明如下所述; 也可以看看PEP 3131了解更多详情。

内的ASCII范围(U + 0001..U + 007F),标识符的有效字符是相同的在Python 2.x的:大写和小写字母A通过 Z,下划线_和,除了第一个字符,数字 09

Python 3.0引入了ASCII范围之外的其他字符(请参阅 PEP 3131)。对于这些字符,分类使用unicodedata模块中包含的Unicode字符数据库的版本。

标识符的长度不受限制。案件很重要。

identifier

 :: =   *

id_start

 :: = <一般类别中的所有字符Lu,Ll,Lt,Lm,Lo,Nl,下划线,以及具有Other_ID_Start属性的字符>

id_continue

 :: = <所有字符,以及中的字符类别Mn,Mc,Nd,Pc和其他具有Other_ID_Continue属性>

xid_start

 :: = < 其NFKC规范化所在的所有字符在“id_start xid_continue *”>

xid_continue

 :: = < 其NFKC规范化所在的所有字符“id_continue * “>

xid_start

xid_continueid_startid_startid_continue

上面提到的Unicode类别代码代表:

  •  – 大写字母
  • Ll – 小写字母
  • Lt – 标题字母
  • Lm – 修饰符字母
  •  – 其他字母
  • Nl – 字母数字
  • Mn – 非间距标记
  • Mc – 间距组合标记
  • Nd – 十进制数
  • Pc – 连接器标点符号
  • Other_ID_Start -字符的显式列表PropList.txt支持向后兼容性
  • Other_ID_Continue – 同样

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

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

 

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。保留的标识符类

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

_*

不是由进口的。特殊标识符在交互式解释器中用于存储上次评估的结果; 它存储在模块中。不处于交互模式时, 没有特殊含义且未定义。请参阅import语句一节。from module import *_builtins_

注意

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

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

 

2.4。文字

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

 

2.4.1。字符串和字节文字

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

stringliteral

 :: = [

stringprefix

](

shortstring

|

longstring

stringprefix

 :: =“r”| “你”| “R”| “你”| “f”| “F” | “fr”| “Fr”| “fR”| “FR”| “rf”| “rF”| “Rf”| “RF”

shortstring

 :: =“'”

shortstringitem

*“'”| '''''

shortstringitem

''

longstring

 :: =“'''”

longstringitem

*“''''| '“”“'

longstringitem

*'”“”'

shortstringitem

 :: =

shortstringchar

|

longstringitem

 :: = |

shortstringchar

 :: = <除“\”或换行符之外的任何源字符或引用>

longstringchar

 :: = <除“\”以外的任何源字符

stringescapeseq

 :: =“\”<任何源字符>

stringescapeseq


 

longstringcharstringescapeseq

bytesliteral

 :: =

bytesprefix

shortbytes

|

longbytes

bytesprefix

 :: =“b”| “B”| “br”| “Br”| “bR”| “BR”| “rb”| “rB”| “Rb”| “RB”

shortbytes

 :: =“'”

shortbytesitem

*“'”| '''''

shortbytesitem

''

longbytes

 :: =“'''”

longbytesitem

*“''''| '“”“'''

longbytesitem

'”“”'

shortbytesitem

 :: =

shortbyteschar

|

longbytesitem

 :: = |

shortbyteschar

 :: = <除“\”或换行符或引号>之外的任何ASCII字符

bytesescapeseq


 

longbytescharbytesescapeseq

不受这些生产指示的一个句法限制是空白是不允许之间stringprefixbytesprefix与文字的其余部分。源字符集由编码声明定义; 如果源文件中没有给出编码声明,则为UTF-8; 请参阅编码声明部分。

用简单的英语:两种类型的文字都可以用匹配的单引号(')或双引号(")括起来。它们也可以包含在三个单引号或双引号的匹配组中(这些通常称为 三引号字符串)。反斜杠(\)字符用于转义具有特殊含义的字符,例如换行符,反斜杠本身或引号字符。

字节文字总是以'b'或为前缀'B'; 它们生成bytes类型的实例而不是str类型。它们可能只包含ASCII字符; 数字值为128或更大的字节必须用转义表示。

字符串和字节文字都可以选择以字母'r' 或前缀为前缀'R'; 这样的字符串称为原始字符串,并将反斜杠视为文字字符。其结果是,在字符串,'\U''\u' 在原始字符串转义不会特殊对待。鉴于Python 2.x的原始unicode文字行为与Python 3.x不同,'ur'语法不受支持。

版本3.3中的新功能:'rb'原始字节文字的前缀已添加为同义词'br'

版本3.3中的新功能:u'value'重新引入了对unicode legacy literal()的支持,以简化双Python 2.x和3.x代码库的维护。看到PEP 414了解更多信息。

'f''F'在其前缀中的字符串文字是 格式化的字符串文字 ; 请参阅格式化字符串文字。该'f'可结合'r',但不与'b''u',因此原始格式的字符串是可能的,但格式字节的文字都没有。

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

除非存在'r''R'前缀,否则字符串和字节文字中的转义序列将根据与标准C使用的规则类似的规则进行解释。已识别的转义序列为:

逃脱序列 含义 笔记
\newline 反斜杠和换行符被忽略
\\ 反斜杠(\
\' 单引号('
\" 双引号("
\a ASCII贝尔(BEL)
\b ASCII退格(BS)
\f ASCII Formfeed(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"\" 不是有效的字符串文字(即使原始字符串不能以奇数个反斜杠结尾)。具体来说,原始文字不能以单个反斜杠结尾 (因为反斜杠会转义以下引号字符)。另请注意,后跟换行符的单个反斜杠被解释为这两个字符作为文字的一部分,而不是行继续。

 

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中的新功能。

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

)* [“,”] |

conversion

 :: =“s”| “r”| “a”

format_spec

 :: =(| NULL | )*

literal_char

 :: = <除“{”,“}”或NULL之外的任何代码点>

yield_expression


 

literal_charreplacement_field

花括号外部的字符串部分按字面处理,除了任何加倍的花括号'{{''}}'用相应的单花括号替换。单个开始花括号'{'标记替换字段,该字段以Python表达式开头。在表达式之后,可能存在由感叹号引入的转换字段'!'。格式说明符也可以附加,由冒号引入':'。替换字段以闭合花括号结束'}'

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

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

然后使用format()协议格式化结果。格式说明符将传递给__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'
>>> today = datetime(year=2017, month=1, day=27)
>>> f"{today:%B %d, %Y}"  # using date format specifier
'January 27, 2017'
>>> number = 1024
>>> f"{number:#0x}"  # using integer format specifier
'0x400'

 

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

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'

 

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

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

 

也可以看看 PEP 498用于添加格式化字符串文字的提案,并且str.format()使用相关格式字符串机制。

 

2.4.4。数字文字

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

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

 

2.4.5。整数文字

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

整数

 :: =

decinteger

|

bininteger

|

octinteger

|

decinteger

 :: = ([“_”] )* | “0”+([“_”]“0”)*

bininteger

 :: =“0”(“b”|“B”)([“_”] )+

octinteger

 :: =“0”(“o” |“O”)([“_”] )+

hexinteger

 :: =“0”(“x”|“X”)([“_”] )+

nonzerodigit

 :: =“1”......“9”

digit

 :: =“0”...“9”

bindigit

 :: =“0”| “1”

octdigit

 :: =“0”......“7”

hexdigit

 :: = | “a”......“f”| “A” ... “F”

hexinteger


 

nonzerodigitdigitbindigitoctdigithexdigitdigit

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

在确定文字的数值时会忽略下划线。它们可用于对数字进行分组以提高可读性。一个下划线可以出现在数字之间,也可以出现在基数说明符之后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

|

pointfloat

 :: = [ ] | “”

exponentfloat

 :: =(| )

digitpart

 :: = ([“_”] )*

fraction

 :: =“。”

exponent

 :: =(“e”|“E”)[“+”| “ - ”]

exponentfloat


 

digitpartfractiondigitpartdigitpartpointfloatexponent

 

digitdigitdigitpart

digitpart请注意,整数和指数部分始终使用基数10进行解释。例如,077e010是合法的,并表示与之相同的数字77e10。允许的浮点文字范围取决于实现。与整数文字一样,数字分组支持下划线。

浮点文字的一些例子:

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

在版本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/11.0.0/ucd/NameAliases.txt