Lexical Analysis
- python将读取的程序问题转换为Unicode码点
- 源文件的文本编码可由编码声明指定
 - 默认UTF-8
 
 - 词法分析器将文件拆分为token
 - 解释器以词法分析器生成的token流作为输入
 
行结构
逻辑行
逻辑行:逻辑行的结束以NEWLINE token表示
- 语句不能跨越逻辑行边集,除非其语法允许包含
NEWLINE(如复合语句包含多个子语句) - 逻辑行可由一个、多个物理行按照明确、隐含行拼接规则 构成
 
python程序可以分为很多逻辑行
物理行
物理行:以行终止序列结束的字符序列
- 源文件、字符串中可以使用任何标准平台上行终止序列
- Unix:
\n换行符LF - Win:
\r\n回车加换行CR LF - Macintosh:
\r回车CR 
 - Unix:
 - 输入结束会被作为最后物理行的隐含终止标志
 
- 嵌入Python源码字符串应使用标准C传统换行符
 \n
显式行拼接
显式行拼接:两个、多个物理行使用\拼接为一个逻辑行
- 物理行以不在字符串、注释内的反斜杠结尾时,将与下行
拼接构成一个单独逻辑行
- 反斜杠、其后换行符将被删除
 
 - 以反斜杠结束的行不能带有注释
 - 反斜杠不能用于
- 拼接注释
 - 拼接字符串外token
 
 - 不允许原文字符串以外反斜杠存在于物理行其他位置
 
隐式行拼接
隐式行拼接
圆括号、方括号、花括号内表达式允许被分为多个物理行,无需 使用反斜杠
- 拼接行可以带有注释
 - 后续行缩进不影响程序结构、允许为空白行
 - 拼接行之间不会有
NEWLINEtoken 
三引号
"""/'''字符串允许被分为多个物理行- 拼接行中不允许带有注释
 
空白行
空白行:只包含空格符、制表符、进纸符、注释的逻辑行会被忽略,
不生成NEWLINE token
交互式输入语句时,对空白行处理可能因为读取-求值-打印循环 的具体实现而存在差异
- 标准交互模式解释器中:完全空白逻辑行将会结束一条多行 复合语句
 
注释
注释:一不包含在字符串内的#开头,在物理行末尾结束
- 注释标志逻辑行的结束,除非存在隐含行拼接规则
 - 注释在语法分析中被忽略,不属于token
 
编码声明
编码声明:位于python脚本第一、第二行,匹配正则表达式
coding[=:]\s*([-\w.]+)的注释将被作为编码声明处理
1  | # -*- coding: <encoding-name> -*-  | 
- 表达式第一组指定了源码文件编码
- 编码声明指定编码名称必须是python所认可的编码
 - 词法分析将使用此编码:语义字符串、注释、标识符
 
 - 编码声明须独占一行,若在第二行,则第一行也必须为注释
 - 没有编码声明,默认编码为UTF-8
- 若文件首字节为UTF-8字节顺序标志
b\xef\xbb\xbf, 文件编码也声明为UTF-8 
 - 若文件首字节为UTF-8字节顺序标志
 
缩进
缩进:逻辑行开头的空白(空格符、制表符)被用于计算该行 的缩进等级,决定语句段落组织结构
- 首个非空白字符之前的空格总数确定该行的缩进层次
- 缩进不能使用反斜杠进行拼接,首个反斜杠之前空格将确定 缩进层次
 
 - 制表符被替换为1-8个空白,使得缩进的空格总数为8倍数
- 源文件若混合使用制表符、空格符缩进,并使得确定缩进
层次需要依赖于制表符对应空格数量设置,将引发
TabError - 由于非Unix平台上文本编辑器本身特性,源文件中混合使用 制表符、空格符是不明智的
 
 - 源文件若混合使用制表符、空格符缩进,并使得确定缩进
层次需要依赖于制表符对应空格数量设置,将引发
 - 进纸符
- 在行首时:在缩进层级计算中被忽略
 - 行首空格内其他位置:效果未定义,可能导致空格计数重置 为0
 
 
- 不同平台可能会显式限制最大缩进层级
 
INDENT/DEDENT token
- 读取文件第一行前,向堆栈中压入零值,不再弹出
 - 被压入栈的层级数值从底至顶持续增加
 - 每个逻辑行开头的行缩进层级将和栈顶进行比较
- 相同:不做处理
 - 新行层级较高:压入栈中,生成
INDENTtoken - 新行层级较低:应为栈中层级数值之一
- 栈中高于该层级所有数值被弹出
 - 每弹出一级数值生成一个
DEDENTtoken 
 
 - 文件末尾,栈中剩余每个大于0数值生成一个
DEDENTtoken 
Tokens
型符
- 空白字符不属于token
- 除逻辑行开头、字符串内,空格符、制表符、进纸符等 空白符均可分隔token
 - 否则彼此相连的token会被解析为一个不同的token
 
 - 若存在二义性,将从左至右尽可能长读取合法字符串组成token
 
Indentifiers
标识符/名称:python标识符语法基于Unicode标准附件UAX-31,有 修改
ASCII字符集内可用于标识符与py2一致
- 大、小写字母
 - 下划线
_ - 数字
0-9 
py3中引入ASCII字符集以外的额外字符
- 其分类使用包含于
unicodedata模块中Unicode字符数据库 版本 
- 其分类使用包含于
 标识符没有长度限制、大小写敏感
1  | identifier ::= xid_start xid_continue*  | 
Lu:大写字母Ll:小写字母Lt:词首大写字母Lm:修饰字母Lo:其他字母Nl:字母数字Mn:非空白标识Mc:含空白标识Nd:十进制数字Pc:连接标点Other_ID_Start:由PropList.txt定义的显式字符列表, 用来支持向后兼容Other_ID_Continue:同上
Keywords
关键字:以下标识符作为语言的保留字、关键字,不能用作普通 标识符
1  | False await else import pass  | 
保留标识符类
以下划线字符开头、结尾的标识符类:具有特殊函数
_*:不会被from module import *导入- 特殊标识符
_:交互式解释器中用于存放最近一次求值 结果,不处于交互模式时无特殊含义、无预定义 
- 特殊标识符
 __*__:系统定义名称- 由解释器极其实现(包括标准库)定义
 - 任何不遵循文档指定方式使用
__*__行为可能导致无警告 出错 
__*:类私有名称- 在类定义中使用
 - 会以混合形式重写避免基类、派生类私有属性之间出现 名称冲突
 
字面值
字面值:表示一些内置类型常量
字符串、字节串字面值
1  | stringliteral ::= [stringprefix](shortstring | longstring)  | 
1  | bytesliteral ::= bytesprefix(shortbytes | longbytes)  | 
stringprefix、bytesprefix与字面值剩余部分之间不允许 由空白符- 源字符集由编码声明定义
 - 字节串字面值只允许ASCII字符(但允许存储不大于256)
 
两种字面值都可以使用成对(连续三个)单引号、双引号标示 首尾
- 单引号
'':允许包含双引号"" - 双引号
"":允许包含单引号'' - 三重引号
'''、"""- 原样保留:未经转义的换行、(非三联)引号、空白符
 
 
- 单引号
 反斜杠
\用于对特殊含义字符进行转义
字符串前缀
b/B前缀:字节串字面值- 创建
bytes类型而非str类型实例 - 只能包含ASCII字符
 - 字节对应数值大于128必须以转义形式表示
 
- 创建
 r/R:原始字符串/字节串- 其中反斜杠
\被当作其本身字面字符处理 - 转换序列不在有效
 - 原始字面值不能以单个
\结束,会转义之后引号字符 
- 其中反斜杠
 f/F:格式化字符串字面值
转义规则
字符串、字节串字面值中转义序列基本类似标准C转义规则
\xhh:必须接受2个16进制数码
以下转义序列仅在字符串字面值中可用
\N{name}:Unicode数据库中名称为name的字符\uxxxx:必须接受4个16进制数码\Uxxxxxxxx:必须接受8个16进制数码
无法识别的转义序列
- py3.6之前将原样保留在字符串中
 - py3.6开始,将引发
DeprecationWarning,未来可能会 引发SyntaxError 
字符串字面值拼接
多个相邻字符串、字符串字面值(空白符分隔),含义等同于 全部拼接为一体
- 所用引号可以彼此不同(三引号风格也可用)
 - 每部分字符串可以分别加注释
 - 可以包括格式化字符串字面值
 
此特性是在句法层面定义,在编译时实现
- 在运行时拼接字符串表达式必须使用
+运算符 
- 在运行时拼接字符串表达式必须使用
 
格式化字符串字面值
格式化字符串字面值:带有f/F前缀的字符串字面值
包含可替换字段,即以
{}标示的格式表达式- 字符串
{}以外部分按字面值处理 - 双重花括号
{ { } }被替换为相应单个花括号 
- 字符串
 格式表达式被当作正常的包含在圆括号中python表达式处理 ,在运行时从左至右被求值
- 不允许空表达式
 lambda空表达式必须显式加上圆括号- 可以包含换行:如三引号字符串
 - 不能包含注释
 - 不能
\反斜杠,考虑创建临时变量 
格式化字符串字面值可以拼接,但是一个替换字段不能拆分到 多个字面值中
格式化字符串不能用作文档字符串,即使其中没有包含表达式
1  | f_string ::= (literal_char | "{{" | "}}" | replacement_field)*  | 
!:标识转换字段!s:对结果调用str()!r:调用repr()!a:调用ascii()
1
2
3
4name = "Fred"
print(f"he said his name is {name!r}")
print("he said his name is {repr(name)}")
# 二者等价::标识格式说明符,结果使用format()协议格式化- 格式说明符被传入表达式或转换结果的
.__format__()方法 - 省略格式说明符则传入空字符串
 
1
2
3
4
5
6
7
8
9width, precision = 4, 10
value = decimal.Deciaml("12.345")
print(f"result: {value: {width}.{precision}}")
today = datetime(yeat=2017, month=1, day=27)
print(f"{today: %B %d, %Y}")
number = 1024
print(f"{number: #0x}")- 格式说明符被传入表达式或转换结果的
 顶层格式说明符可以包含嵌套替换字段
- 嵌套字段可以包含有自身的转换字段、格式说明符,但不能 包含更深层嵌套替换字段
 
数字字面值
- 数字字面值不包括正负号
- 负数实际上是由单目运算符
-和字面值组合而成 
 - 负数实际上是由单目运算符
 - 没有专门复数字面值
- 复数以一对浮点数表示
 - 取值范围同浮点数
 
 - 数字字面值可以使用下划线
_将数码分组提高可读性- 确定数字大小时,字面值之间的下滑线被忽略
 - 下划线可以放在数码之间、基数说明符
0x等之后 - 浮点数中不能直接放在
.后 
 
整形数字字面值
- 整形数字字面值没有长度限制,只受限于内存
 
1  | integer ::= decinteger | bininteger | octinteger | hexinteger  | 
浮点数字字面值
- 整形数部分、指数部分解析时总以10为计数
 - 浮点数字面值允许范围依赖于具体实现
 
1  | floatnumber ::= pointfloat | exponentfloat  | 
虚数字面值
- 序数字面值将生成实部为
0.0的复数 
1  | imagnumber ::= (floatnumber | digitpart) ("j" | "J")  | 
运算符
1  | + - * ** / // % @  | 
分隔符
1  | ( ) [ ] { }  | 
- 以上列表中后半部分为增强赋值操作符
- 在词法中作为分隔符,同时起运算作用
 
 
1  | ' " # \  | 
- 以上可打印ASCII字符
- 作为其他token组成部分时有特殊意义
 - 或对词法分析器有特殊意义
 
 
1  | $ ?  | 
- 以上可打印ASCII字符不再Python词法中使用
- 出现在字符串字面值、注释之外将无条件引发错误