Simple Statements

简单语句:由单个逻辑行构成

  • 多条简单语句可以在同一物理行内、并以分号分隔
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
simple_stmt ::=  expression_stmt
| assert_stmt
| assignment_stmt
| augmented_assignment_stmt
| annotated_assignment_stmt
| pass_stmt
| del_stmt
| return_stmt
| yield_stmt
| raise_stmt
| break_stmt
| continue_stmt
| import_stmt
| future_stmt
| global_stmt
| nonlocal_stmt

Expression Statements

表达式语句:用于计算、写入值(交互模式下),或调用过程(不 返回有意义结果的函数)

1
expresssion_stmt ::= starred_expression
  • 用途:表达式语句对指定表达式[列表]进行求值
  • 交互模式下
    • 若值不为None:通过内置repr()函数转换为字符串, 单独一行写入标准输出
    • 值为None:不产生任何输出

Assignment Statements

赋值语句:将名称[重]绑定到特定值、修改属性或可变对象成员项

1
2
3
4
5
6
7
8
9
assignment_stmt ::=  (target_list "=")+ (starred_expression | yield_expression)
target_list ::= target ("," target)* [","]
target ::= identifier
| "(" [target_list] ")"
| "[" [target_list] "]"
| attributeref
| subscription
| slicing
| "*" target
  • 用途:对指定表达式列表求值,将单一结果对象从左至右逐个 赋值给目标列表

  • 赋值根据目标列表的格式递归定义,目标为可变对象组成部分时 (属性引用、抽取、切片),可变对象赋值有效性会被检查, 赋值操作不可接受可能引发异常

  • 赋值顺序:将赋值看作是左、右端项重叠

    • 根据定义赋值语句内多个赋值是同时重叠,如:a,b=b,a 交换两变量值
    • 但赋值语句左端项包含集合类型时,重叠从左到右依次执行

      1
      2
      3
      4
      x = [0,1]
      i = 0
      i, x[i] = 1, 2
      # `x`现在为`[0, 2]`
      • LOAD_XXX指令将右端项从左到右依次压栈
      • ROT_N指令交换栈顶元素
      • STORE_XXX指令将栈顶元素弹出从左到右依次给 右端项赋值
      • 以上语句中,计算表达式x[i]i已经被赋新值
      • dis.dis()查看代码块指令

赋值目标为列表

赋值目标(左端项)为列表(可选包含在圆括号、方括号内)

  • 若目标列表为不带逗号、可以包含在圆括号内的单一目标,将 右端项赋值给该目标

  • 否则:右端项须为与目标列表相同项数的可迭代对象,其中 元素将从左至右顺序被赋值给对应目标

  • 若目标列表包含带*元素:则类似实参解包,其须为可迭代 对象,且右端项至少包含目标列表项数-1

    • 加星目标前元素被右端项前段元素一一赋值
    • 加星目标后元素被右端项后段元素一一赋值
    • 加星目标被赋予剩余目标元素构成的列表

赋值目标为单个目标

目标为标识符(名称)

  • 名称未出现在当前代码块globalnonlocal语句中:名称 被绑定到/赋值为当前局部命名空间对象

  • 否则:名称被分别绑定到/赋值为全局命名空间、或nonlocal 确定的外层命名空间中对象

  • 若名称已经被绑定则被重新绑定,可能导致之前被绑定名称 对象引用计数变为0,对象进入释放过程并调用其析构器

目标为属性引用

  • 引用中原型表达式被求值:应产生具有可赋值属性的对象, 否则raise TypeError

  • 该对象指定属性将被赋值,若无法赋值将 raise AttributeError

目标为抽取项

  • 引用中原型表达式被求值:应产生可变序列对象(列表)、 映射对象(字典)

  • 引用中抽取表达式被求值

    • 若原型表达式求值为可变序列对象

      • 抽取表达式产生整数,包含负数则取模,结果只须为 小于序列长度非负整数
      • 整数指定的索引号的项将被赋值
      • 若索引超出范围将raise IndexError
    • 若原型表达式求值为映射对象

      • 抽取表达式须产生与该映射键类型兼容的类型
      • 映射可以创建、更新抽取表达式指定键值对
  • 对用户自定义对象,将调用__setitem__方法并附带适当参数

目标为切片

  • 引用中原型表达式被求值:应当产生可变序列对象

    • 右端项应当是相同类型的序列对象
  • 上界、下界表达式若存在将被求值

    • 其应为整数,若为负数将对原型表达式序列长度求模,最终 边界被裁剪至0、序列长度开区间中
    • 默认值分别为零、序列长度
  • 切片被赋值为右端项

    • 若切片长度和右端项长度不同,将在目标序列允许情况下 改变目标序列长度

Augmented Assignment Statements

增强赋值语句:在单个语句中将二元运算和赋值语句合为一体

1
2
3
4
augmented_assignment_stmt ::=  augtarget augop (expression_list | yield_expression)
augtarget ::= identifier | attributeref | subscription | slicing
augop ::= "+=" | "-=" | "*=" | "@=" | "/=" | "//=" | "%=" | "**="
| ">>=" | "<<=" | "&=" | "^=" | "|="
  • 增强赋值语句不能类似普通赋值语句为可迭代对象拆包
  • 赋值增强语句对目标和表达式列表求值

    • 依据两操作数类型指定执行二元运算
    • 将结果赋给原始目标
  • 增强赋值语句可以被改写成类似、但非完全等价的普通赋值语句

    • 增强赋值语句中目标仅会被求值一次
    • 在可能情况下,运算是原地执行的,即直接修改原对象而 不是创建新对象并赋值给原对象
    • 所以增强赋值先对左端项求值
  • 其他增强赋值语句和普通赋值语句不同点

    • 单条语句中对元组、多目标赋值赋值操作处理不同

Annotated Assignment Statements

带标注的赋值语句:在单个语句中将变量、或属性标注同可选赋值 赋值语句合并

1
annotated_assignment_stmt ::=  augtarget ":" expression ["=" expression]
  • 与普通赋值语句区别仅在于:仅有单个目标、且仅有单个右端项 才被允许

  • 在类、模块作用域中

    • 若赋值目标为简单名称,标注会被存入类、模块的 __annotations__属性中
    • 若赋值目标为表达式,标注被求值但不会被保存
  • 在函数作用域内,标注不会被求值、保存

  • 若存在右端项,带标注赋值在对标注值求值前执行实际赋值; 否则仅对赋值目标求值,不执行__setitem____setattr__

    • 参见cs_python/py3ref/#todo

关键字语句

assert

assert语句:在程序中插入调试性断言的简便方式

1
assert_stmt ::= "assert" expression ["," expression]
  • assert语句等价于

    1
    2
    3
    4
    5
    6
    7
    8
    9
    if __debug__:
    if not expression:
    raise AssertionError
    # 等价于`assert expression`

    if __debug__:
    if not expression1:
    raise AssertionError(expression2)
    # 等价于`assert expression1, expression2`
    • 无需再错误信息中包含失败表达式源码,其会被作为栈追踪 一部分被显示
  • 假定__debug__AssertionError指向具有特定名称的内置 变量,当前实现中

    • __debug__赋值是非法的,其值在解释器启动时确定
    • 默认内置变量__debug__=True
    • 请求优化时__debug__置为False
      • -0命令行参数开启
      • 若编译时请求优化,代码生成器不会为assert语句生成 代码

pass

pass语句:空操作,被执行时无事情发生

1
pass_stmt ::= "pass"
  • 适合语法上需要语句、但不需要执行代码时临时占位

del

del语句:从局部、全局命名空间中移除名称的绑定,若名称未绑定 将raise NameError

1
del_stmt ::= "del" target_list
  • 删除是递归定义的

    • 类似赋值的定义方式
    • 从左至右递归的删除目标列表中每个目标
  • 属性、抽取、切片的删除会被传递给相应原型对象

    • 删除切片基本等价于赋值为目标类型的空切片???

return

return语句:离开当前函数调用,返回列表表达式值、None

  • 在生成器函数中,return语句表示生成器已完成

    • raise StopIteration
    • 返回值作为参数构建StopIteration,并作为 StopIteration.value属性
  • 异步生成器函数中,return语句表示异步生成器已完成

    • raise StopAsyncIteration
    • 非空return返回值在将导致语法错误
1
return_stmt ::= "return" [expression_list]
  • return语法上只会出现于函数定义代码块中,不会出现于 类定义所嵌套代码中

  • 若提供表达式列表,其将被求值;否则缺省为None

  • return将控制流传出带有finally子句的try语句时, finally子句会先被执行然后真正离开函数

yield

yield语句:语义上等于yield表达式

1
yield_stmt ::= yield_expression
  • 可用于省略在使用等效yield表达式必须的圆括号

    1
    2
    3
    4
    5
    6
    yield <expr>
    yield from <expr>
    # 以上yield语句、以下yield表达式等价

    (yield <expr>)
    (yield from <expr>)
  • yeild表达式、语句仅在定义[异步]生成器函数时使用,且仅 用于函数体内部,且函数体包含yield就使得该定义创建 生成器函数而非普通函数

  • yield表达式参见cs_python/py3ref/#todo

raise

raise语句:引发异常

1
raise_stmt ::= "raise" [expression ["from" expression]]
  • 若不带表达式:raise将重新引发当前作用域最后一个激活 异常

    • 若当前作用域内没有激活异常,将引发RuntimeError 提示错误
  • 否则计算表达式作为异常对象

    • 异常对象须为BaseException子类、实例
    • 若其为类,则通过不带参数的实例化该类作为异常实例
  • 异常被引发时会自动创建回溯对象,并被关联至可写 __traceback__属性

    • 可以创建异常时同时使用.with_traceback()异常方法 自定义回溯对象

      1
      raise Exception("foo occured").with_traceback(tbobj)

异常串联

  • from子句用于异常串联:其后表达式求值需要为另一个异常

    • 其将作为可写__cause__属性被关联到引发的第一个异常
    • 若引发异常未被处理,两个异常都将被打印
  • 若异常在try语句中finally子句、其他子句后、先中引发, 类似机制发挥作用,之前异常被关联到新异常的__context__ 属性

  • 异常串联可以通过在from子句中指定None显式抑制

break

break语句:终结最近外层循环、循环的可选else子句

1
break_stmt ::= "break"
  • break在语法上只出现在forwhile所嵌套代码

    • 不包括循环内函数定义、类定义所嵌套代码
  • for循环被break终结,循环控制目标保持当前值

  • break将控制流传出带有finally子句的try语句时, finally子句会先被执行,然后真正离开循环

continue

continue语句:继续执行最近外层循环的下一伦茨

1
continue_stmt ::= "continue"
  • continue在语法上只出现在forwhile所嵌套代码

    • 不包括循环内函数定义、类定义、finally子句所嵌套 代码
  • continue将控制流传出带有finally子句的try语句时, finally子句会先被执行,然后真正开始循环下一个轮次

import

import语句

1
2
3
4
5
6
7
8
import_stmt     ::=  "import" module ["as" identifier] ("," module ["as" identifier])*
| "from" relative_module "import" identifier ["as" identifier]
("," identifier ["as" identifier])*
| "from" relative_module "import" "(" identifier ["as" identifier]
("," identifier ["as" identifier])* [","] ")"
| "from" module "import" "*"
module ::= (identifier ".")* identifier
relative_module ::= "."* module | "."+
  • 基本import子句执行步骤

    • 查找模块,若有必要加载并初始化模块
    • import所处作用域的局部命名空间定义名称
    • 语句包含多个子句(逗号分隔)时,以上两个步骤将分别 对每个子句执行,如同子句被分成独立import语句
  • 默认情况下,导入的父模块中命名空间中不包含子模块属性, 即导入父模块不能直接通过属性.引用子模块

    • 有些包会在父模块中导入子模块,则初始化模块时父模块 中即有子模块属性
    • 在当前模块手动导入子模块,子模块绑定至父模块命名空间 中同名属性
  • 导入机制参见cs_python/py3ref/import_system

绑定

  • 若模块名称后带有as,则在as之后名称将直接绑定到所导入 模块

  • 若没有指定其他名称、且被导入模块为最高层级模块,则模块 名称被绑定到局部命名空间作为对所导入模块的引用

  • 若被导入模块不是最高级模块,则包含该模块的最高层级包名将 被绑定到局部命名空间作为的该最高层级包的引用,所导入模块 必须使用完整限定名称访问而不能直接访问

from子句

  • 查找from子句中指定模块,若有必要则加载并初始化模块

  • import子句中指定的每个标识符

    • 检查被导入模块是否有该名称属性
    • 若没有,尝试导入具有该名称子模块,然后再次检查 被导入(上级)模块是否具有该属性
    • 若未找到该属性,则raise ImportError
    • 否则将对该值引用存入局部命名空间,若有as子句则 使用其指定名称,否则使用该属性名称
*通配符

标识符列表为通配符*形式:模块中定义的全部公有名称都被绑定 至import语句作用域对应局部命名空间

  • 模块命名空间中__all__属性:字符串列表,指定模块 定义的公有名称

    • 其中字符串项为模块中定义、导入的名称
    • 其中中所给出的名称被视为公有、应当存在
    • 应该包含所有公有API、避免意外导出不属于API部分项
  • __all__属性未定义:则公有名称集合将包括在模块 命名空间中找到的、所有不以_开头名称

  • 通配符模式仅在模块层级允许使用,在类、函数中使用将 raise SyntaxError
相对导入

相对导入:指定导入模块时,无需指定模块绝对名称

  • 需要导入的模块、包被包含在同一包中时,可在相同顶级包中 进行相对导入,无需指明包名称

  • from子句中指定的模块、包中使用前缀点号指明需要上溯 包层级数

    • 一个前缀点号:执行导入的模块在当前包
    • 两个前缀点号:上溯一个包层级
    • 三个前缀点号:上溯两个包层级,依此类推

      1
      form ...sub_sub_pkg import mod1
  • 相对导入可以避免模块之间产生冲突,适合导入相关性强代码

    • 脚本模式(在命令行中执行.py文件)不支持相对导入
    • 要跨越多个文件层级导入,只需要使用多个.,但 PEP 328建议,相对导入层级不要超过两层

future语句

future语句:指明莫格特定模块使用在特定、未来某个python发行版 中成为标准特性的语法、语义

1
2
3
4
5
future_stmt ::=  "from" "__future__" "import" feature ["as" identifier]
("," feature ["as" identifier])*
| "from" "__future__" "import" "(" feature ["as" identifier]
("," feature ["as" identifier])* [","] ")"
feature ::= identifier
  • import __future__ [as name]:不是future语句,只是没有 特殊语义、语法限制的普通import语句
  • 用途

    • 允许模块在包含新特性发行版前使用该特性
    • 目的是使得在迁移至引入不兼容改变的python未来版本 更容易
  • future语句是针对编译器的指令

    • 在编译时被识别并做特殊对待

      • 改变核心构造语义常通过生成不同代码实现
      • 新特性可能会引入不兼容语法,如:新关键字,编译器 可能需要以不同方式解析模块
    • 编译器需要知道哪些特性名称已经定义

      • 包含未知特性的future语句将引发编译时错误
    • 直接运行时语义同其他import语句

      • 相应运行时语义取决于future语句启用的指定特性
    • 在包含future语句的环境中,通过exec()compile() 调用代码会使用future语句关联的语法、语义,此行为 可以通过compile()可选参数加以控制
  • future语句必须在靠近模块开头位置处出现,可以出现在future 语句前的行

    • 模块文档字符串
    • 注释
    • 空行
    • 其他future语句

global

global语句:声明所列出标识符将被解读为全局变量

1
global_stmt ::= "global" identifier ("," identifier)*
  • global语句是作用于整个当前代码块的声明

  • 局部作用域中给全局变量赋值必须用到global关键字

    • 仅仅是获取值无需global语句声明
    • 但自由变量也可以指向全局变量而不必声明为全局变量
  • global语句中列出的名称

    • 不能被定义为形参名
    • 不能作为for循环控制目标
    • 不能出现在类定义、函数定义、import语句、变量标注中
    • CPython:暂时未强制要求上述限制,未来可能更改
  • global是对解释器的指令,仅对与global语句同时被解析 的代码起作用

    • 包含在作为exec()参数的字符串、代码对象中global 语句不会影响exec()所在代码块
    • 反之exec()中代码也不会被调用其代码块影响
    • eval()compile()等函数同

nonlocal

nonlocal语句:使得列出的名称指向之前最近的包含作用域中 绑定的、除全局变量外的变量

1
nonlocal_stmt ::= "nonlocal" indentifier ("," identifier)*
  • nonlocal语句允许被封装代码重新绑定局部作用域以外、且非 全局(模块)作用域当中变量

    • 即nonlocal语句中列出名称,必须指向之前存在于 包含作用域中的绑定

    • nonlocal语句中列出名称不能与之前存在的局部作用域中 绑定冲突

Compound Statements

复合语句

复合语句:包含其他语句(语句组)的语句

  • 复合语句由一个、多个子句组成,子句包含句头、句体

    • 子句头
      • 都处于相同的缩进层级
      • 以作为唯一标识的关键字开始、冒号结束
    • 子句体
      • 在子句头冒号后、与其同处一行的一条或多条分号分隔 的多条简单语句
      • 或者是在其之后缩进的一行、多行语句,此形式才能 包含嵌套的复合语句
  • 其会以某种方式影响、控制所包含的其他语句执行

1
2
3
4
5
6
7
8
9
10
11
12
13
compound_stmt ::=  if_stmt
| while_stmt
| for_stmt
| try_stmt
| with_stmt
| funcdef
| classdef
| async_with_stmt
| async_for_stmt
| async_funcdef
suite ::= stmt_list NEWLINE | NEWLINE INDENT statement+ DEDENT
statement ::= stmt_list NEWLINE | compound_stmt
stmt_list ::= simple_stmt (";" simple_stmt)* [";"]
  • 语句总以NEWLINE结束,之后可能跟随DEDENT
  • 可选的后续子句总是以不能作为语句开头的关键字作为开头, 不会产生歧义

关键字

if

if语句:有条件的执行

1
2
3
if_stmt ::=  "if" expression ":" suite
("elif" expression ":" suite)*
["else" ":" suite]
  • 对表达式逐个求值直至找到真值,在子句体中选择唯一匹配者 执行
  • 若所有表达式均为假值,则else子句体如果存在被执行

while

while语句:在表达式保持为真的情况下重复执行

1
2
while_stmt ::=  "while" expression ":" suite
["else" ":" suite]
  • 重复检验表达式
    • 若为真,则执行第1个子句体
    • 若为假,则else子句体存在就被执行并终止循环
  • 第1个子句体中break语句执行将终止循环,且不执行else 子句体
  • 第1个子句体中continue语句执行将跳过子句体中剩余部分, 直接检验表达式

for

for语句:对序列(字符串、元组、列表)或其他可迭代对象中元素 进行迭代

1
2
for_stmt ::= "for" target_list "in" expression_list ":" suite
["else" : suite]
  • 表达式列表被求值一次

    • 应该产生可迭代对象
    • python将为其结果创建可迭代对象创建迭代器
  • 迭代器每项会按照标准赋值规则被依次赋值给目标列表

    • 为迭代器每项执行依次子句体
    • 所有项被耗尽raise StopIteration时,else子句体 存在则会被执行
  • 目标列表中名称在循环结束后不会被删除

    • 但若序列为空,其不会被赋值
  • 序列在循环子句体中被修改可能导致问题

    • 序列的__iter__方法默认实现依赖内部计数器和序列长度 的比较
    • 若在子句体中增、删元素会使得内部计数器“错误工作”
    • 可以对整个序列使用切片创建临时副本避免此问题
  • 第1个子句体中break语句执行将终止循环,且不执行else 子句体
  • 第1个子句体中continue语句执行将跳过子句体中剩余部分, 转至下个迭代项执行

try

try语句:为一组语句指定异常处理器、清理代码

1
2
3
4
5
6
7
try_stmt  ::=  try1_stmt | try2_stmt
try1_stmt ::= "try" ":" suite
("except" [expression ["as" identifier]] ":" suite)+
["else" ":" suite]
["finally" ":" suite]
try2_stmt ::= "try" ":" suite
"finally" ":" suite

except子句

except子句:指定一个、多个异常处理器

  • try子句中没有异常时,没有异常处理器执行

  • 否则,依次检查except子句直至找到和异常匹配的子句

    • 无表达式子句必须是最后一个,将匹配任何异常
    • 有表达式子句中表达式被求值,求值结果同异常兼容则匹配 成功
      • 若在表达式求值引发异常,则对原异常处理器搜索取消
      • 其被视为整个try语句引发异常,将在周边代码、 主调栈中为新异常启动搜索
    • 若无法找到匹配的异常子句,则在周边代码、主调栈中继续 搜索异常处理器
    • 兼容:是异常对象所属类、基类,或包含兼容异常对象元组
  • 当找到匹配except子句时

    • 异常将被赋值给as子句后目标,若存在as子句
    • 对应子句体被执行(所有except子句都需要子句体)
    • as后目标在except子句结束后被清除

      1
      2
      3
      4
      5
      6
      7
      8
      except E as N:
      foo
      # 被转写为
      except E as N:
      try:
      foo
      finally:
      del N
      • 避免因异常附加回溯信息而形成栈帧的循环引用,使得 所有局部变量存活直至下次垃圾回收
      • 则异常必须赋值给其他名称才能在except子句后继续 引用
  • except子句体执行前,有关异常信息存放在sys模块中, 参见cs_python/py3std/os_sys.md

else子句

else子句:在以下情况将被执行,若存在

  • 控制流离开try子句体没有引发异常
  • 没有执行returncontinuebreak语句

finally子句

finally子句:指定清理处理器,子句体在任何情况下都被执行

  • 执行期间程序不能获取任何异常信息

    • tryexceptelse子句中引发的任何未处理异常 将被临时保存,执行完finally子句后被重新引发

    • 但若finally子句中执行returnbreak语句,则临时 保存异常被丢弃

    • finally子句引发新的异常,临时保存异常作为新异常 上下文被串联

    • 显式异常串联参见cs_python/py3ref/simple_stmt
  • try子句中执行returnbreakcontinue语句时, finally子句在控制流离开try语句前被执行

    • 函数返回值由最后被执行return语句决定,而 finally子句总是最后被执行

      1
      2
      3
      4
      5
      6
      7
      8
      def foo():
      try:
      return "try"
      finally:
      return "finally"

      foo()
      # 返回"finally"

with

with语句:包装上下文管理器定义方法中代码块的执行

1
2
with_stmt ::=  "with" with_item ("," with_item)* ":" suite
with_item ::= expression ["as" target]
  • with句头中有多个项目,被视为多个with语句嵌套处理多个 上下文管理器

    1
    2
    3
    4
    5
    6
    with A() as a, B() as b:
    suite
    # 等价于
    with A() as a:
    wiht B() as b:
    suite

执行流程

  • 对表达式求值获得上下文管理器
  • 载入上下文管理器__exit__以便后续使用
  • 调用上下文管理器__enter__方法
  • 若包含as子句,__enter__返回值将被赋值给其后目标
    • with语句保证若__enter__方法返回时未发生错误, __exit__总会被执行
    • 若在对目标列表赋值期间发生错误,视为在语句体内部发生 错误
  • 执行with语句体
  • 调用上下文关管理器__exit__方法
    • 若语句体退出由异常导致
      • 其类型、值、回溯信息将被作为参数传递给__exit__ 方法;否则提供三个None作为参数
      • __exit__返回值为假,该异常被重新引发;否则 异常被抑制,继续执行with之后语句
    • 若语句体由于异常以外任何原因退出
      • __exit__返回值被忽略

Function

函数定义:对用户自定义函数的定义

1
2
3
4
5
6
7
8
9
10
11
12
funcdef                 ::=  [decorators] "def" funcname "(" [parameter_list] ")"
["->" expression] ":" suite
decorators ::= decorator+
decorator ::= "@" dotted_name ["(" [argument_list [","]] ")"] NEWLINE
dotted_name ::= identifier ("." identifier)*
parameter_list ::= defparameter ("," defparameter)* ["," [parameter_list_starargs]]
| parameter_list_starargs
parameter_list_starargs ::= "*" [parameter] ("," defparameter)* ["," ["**" parameter [","]]]
| "**" parameter [","]
parameter ::= identifier [":" expression]
defparameter ::= parameter ["=" expression]
funcname ::= identifier
  • 函数定义是可执行语句

    • 在当前局部命名空间中将函数名称绑定至函数对象(函数 可执行代码包装器)
    • 函数对象包含对当前全局命名空间的引用以便调用时使用
  • 函数定义不执行函数体,仅函数被调用时才会被执行

Decorators

装饰器:函数定义可以被一个、多个装饰器表达式包装

  • 函数被定义时将在包含该函数定义作用域中对装饰器表达式求值 ,求值结果须为可调用对象
  • 其将以该函数对象作为唯一参数被调用;返回值将被绑定至函数 名称
  • 多个装饰器会以嵌套方式被应用
1
2
3
4
5
6
7
8
@f1(arg)
@f2
def func():
pass
# 大致等价,仅以上不会临时绑定函数对象至名称
def func():
pass
func = f1(arg)(f2(func))

Parameter Types

形参类型

  • POSITIONAL_OR_KEYWORD:之前没有VAR_POSITIONAL类型的 参数

    • 可以通过位置关键字传值
  • KEYWORD_ONLY:之前存在VAR_POSITION类型、或*的参数

    • 只能通过关键字传值
  • VAR_POSITIONAL*args形式参数

    • 只能通过位置传值
    • 隐式默认值为()
  • VAR_KEYWORD**kwargs形式参数

    • 只能通过关键字传值
    • 隐式默认值为{}
  • POSITIONAL_ONLY:只能通过位置传值的参数

    • 某些实现可能提供的函数包含没有名称的位置参数
    • 唯一不能使用关键字传参参数类型
    • CPython:C编写、PyArg_ParseTuple()解析参数的函数

Default Parameters Values

默认参数值:具有parameter = expression形式的形参

  • 具有默认值的形参,对应argument可以在调用中可被省略

  • 默认形参值将在执行函数定义时按从左至右的顺序被求值

    • 即函数定义时的预计算值将在每次调用时被使用
    • 则被作为默认值的列表、字典等可变对象将被所有未指定该 参数调用共享,应该避免
      • 可以设置默认值为None,并在函数体中显式测试
  • POSITION_OR_KEYWORD、有默认值形参必须位于无默认值者后 ,即若形参具有默认值,后续所有在*前形参必须具有默认值
  • KEYWORD_ONLY、有默认值形参可位于无默认值者前

Annotations

  • 形参标注:param:expression
  • 函数返回标注:-> expression
  • 标注不会改变函数语义
  • 标注可以是任何有效python表达式
    • 默认在执行函数定义时被求值
    • 使用future表达式from __future__ import annotations ,则标注在运行时被保存为字符串以启用延迟求值特性
  • 标注默认存储在函数对象__annotation__属性字典中
    • 可以通过对应参数名称、"return"访问

Class

类定义:对类的定义

1
2
3
classdef    ::=  [decorators] "class" classname [inheritance] ":" suite
inheritance ::= "(" [argument_list] ")"
classname ::= identifier
  • 类定义为可执行语句

    • 继承列表inheritance通常给出基类列表、元类
    • 基类列表中每项都应当被求值为运行派生子类的类
    • 没有继承类列表的类默认继承自基类object
  • 类定义语句执行过程

    • 类体将在新的执行帧中被执行
    • 使用新创建的局部命名空间和原有的全局命名空间
    • 类体执行完毕之后
      • 丢弃执行帧
      • 保留局部命名空间
    • 创建类对象
      • 给定继承列表作为基类
      • 保留的局部命名空间作为属性字典__dict__
    • 类名称将在原有的全局命名空间中绑定至该类对象
  • 类可以类似函数一样被装饰

    • 装饰器表达式求值规则同函数装饰器
    • 结果被绑定至类名称
  • 类中属性、方法参见#todo
  • 类属性可以作为实例属性的默认值,但注意使用可变类型值可能 导致未预期结果

Coroutine

协程函数

1
2
async_funcdef ::=  [decorators] "async" "def" funcname "(" [parameter_list] ")"
["->" expression] ":" suite
  • 协程函数可以在多个位置上挂起(保存局部状态)、恢复执行
  • 协程函数体内部
    • awaitasync是保留关键字
    • await表达式、async forasync with只能在协程 函数体内部使用
    • 使用yield from表达式将raise SyntaxError

async for语句

1
async_for_stmt ::= "async" for_stmt
  • async for语句允许方便的对异步迭代器进行迭代

    1
    2
    3
    4
    async for TARGET in ITER:
    ...BLOCK1...
    else:
    ...BLOCK2...

    在语义上等价于

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    iter = (ITER)
    iter = type(iter).__aiter__(iter)
    running = True
    while running:
    try:
    TARGET = await type(iter).__anext__(iter)
    except StopAsyncIteration:
    running = False
    else:
    BLOCK1
    else:
    BLOCK2

async with语句

1
async_with_stmt ::= "async" with_stmt
  • async with语句允许方便使用异步上下文管理器

    1
    2
    async with EXPR as VAR:
    BLOCK

    语义上等价于

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    mgf = (EXPR)
    aexit = type(mgr).__aexit__
    aenter = type(mgr).__aenter__(mgr)

    VAR = await aenter
    try:
    BLOCK
    except:
    if not await aexit(mgr, *sys.exc_info()):
    raise
    else:
    await aexit(mgr, None, None, None)