SBT

综述

SBT:simple build tools,Scala世界的Maven

  • 下载、解压、将SBT_HOME/bin添加进$PATH即可
  • SBT中包含的Scala可通过$ scala console交互式启动解释器

参数命令

  • clean:删除所有构建文件
  • compile:编译源文件:src/main/scalasrc/main/java
  • test:编译运行所有测试
  • console:进入包含所有编译文件、依赖的classpath的scala 解释器
    • :q[uit]:退出
  • run <args>:在和SBT所处同一虚拟机上执行项目main class
  • package:将src/main/resourcessrc/main/scalasrc/main/java中编译出class打包为jar
  • help <cmd>:帮助
  • reload:重新加载构建定义:build.sbtproject.scalaproject.sbt
  • inspect <key>:查看key描述
  • show <key[:subkey]>:查看key对应value执行结果
    • 例:show compile:dependencyClasspath
  • 以上命令可以在shell中作参数、在SBT命令行作命令
  • SBT命令行中历史记录查找同shell
  • ~前缀执行命令表示监视变化,源文件改变时自动执行该命令

项目结构

  • srcsrc中其他目录将被忽略

    • main:主源码目录
      • scala
      • java
      • resources:存储资源,会被打进jar包
        • 分布式执行则每个节点可以访问全体资源,相当于 broadcast
        • 访问时resources作为根目录,直接/列出相对 resource路径
    • test:测试源码目录
      • scala
      • java
      • resources
  • project:可以包含.scala文件,和.sbt文件共同构成 完整构建定义

    • Build.scala
    • plugins.sbt:添加sbt插件
  • target:包含构建出的文件:classes、jar、托管文件、 caches、文档

  • lib:存放非托管依赖

    • 其中jar被添加进CLASSPATH环境变量
  • build.sbt:包含构建定义

    • 其中隐式导入有
      • sbt.Keys._:包含内建key
      • sbt._

SBT配置

1
2
3
4
5
6
7
8
9
10
11
12
13
 # ~/.sbt/repositories
# 默认依赖仓库设置

[repositories]
local
<maven_repo_name>: <repo_address>
<ivy_repo_naem>: <repo_address>, <address_formation>

# 地址格式可能如下
[orgnanization]/[module]/(scala_[scalaVersion]/)(sbt_[sbtVersion]/)[revision]/[type]s/[artifact](-[classifier]).[ext]

# ali Maven2 repo
aliyun: https://maven.aliyun.com/nexus/content/groups/public/
  • 需要添加sbt启动参数-Dsbt.override.build.repos=true使 覆盖默认生效

Build Definition

构建定义:定义在build.sbt中、包含项目构建信息

  • 多工程.sbt构建定义:可为代码中定义多个子项目,结构灵活
  • bare .sbt构建定义
  • 构建定义可以包含位于project目录下的.scala下的文件, 用于定义常用函数、值

多工程.sbt构建定义

1
2
3
4
5
6
7
8
9
10
11
12
13
// 自定义`TaskKey`
lazy val hello = taskKey[Unit]("example task")
// 定义库ID
val derby = "org.apache.derby" % "derby" % "10.4.1.3"

// 创建名为`root`、位于当前目录的子项目
lazy val root = (project in file("."))
// 在`.settings`方法中设置*setting expression*键值对
.settings(
name := "hello",
hello := {prinln("hello")},
libraryDependencies += derby
)
  • 构建定义拥有不可变的Setting[T]类型映射描述项目属性, 是会影响sbt保存键值对的map的转换

    • T:映射表中值类型
    • Setting描述.settings是对映射表的转换,增加新键值、 追加至已有值,转换后的映射成为sbt新映射
  • build.sbt文件中除设置外,可以包含valdef定义

    • 所有定义都在设置之前被计算,无论在文件中所处位置
    • 一般使用lazy val避免初始化顺序问题

Bare.sbt构建定义

1
2
3
4
name := "hello"
version := "1.0"
scalaVersion := "2.12.8"
library.Dependencies += "org.apache.derby" % "derby" % "10.4.1.3"
  • bare .sbt构建定义由Setting[_]表达式列表组成

Keys

  • SettingKey[T]:值仅在子项目载入时计算一次
  • TaskKey[T]:值每次都需要被计算,可能有副作用
  • InputKey[T]:值以命令行参数作为输入的任务
  • 可以通过各自创建方法创建自定义key

    1
    2
    // 给定value(返回)类型、键值对描述
    lazy val hello = taskKey[Unit]("task key demo")
  • 在sbt交互模式下,可以输入key名称获取、执行value

    • setting key:获取、显示value
    • task key:执行value,但不会显示执行结果,需要 show <task>才会打印执行结果
  • Key可以视为为项目定义的属性、trigger
  • taskiness(每次执行)可以视为task key的属性

sbt.Keys内建Key

  • 内建Key中泛型参数已经确定,定制需要满足类型要求

项目属性

  • name:项目名称,默认为项目变量名
  • baseDirectory:项目根目录
  • sourceDirectories:源码目录
    • compile:_:编译时设置
  • sourceDirectory:源码上层目录?

依赖相关

  • unmanageBase:指定非托管依赖目录
  • unmanagedJars:列举unmanagedBase目录下所有jar 的task key
  • dependencyClasspath:非托管依赖classpath
    • compile:_:编译时设置
    • runtime:_:运行时设置
  • libraryDependecies:指定依赖、设置classpath
    • 直接列出
    • Maven POM文件
    • Ivy配置文件
  • resolvers:指定额外解析器,Ivy搜索服务器指示
  • externalResolvers:组合resolvers、默认仓库的task key
    • 定制其以覆盖默认仓库

运行相关

  • package:打包系列Task

    • 类型:TaskKey[File]的task key
    • 返回值:生成的jar文件
  • compile:编译系列Task

.sbt特殊方法

  • 常用类型String等的方法仅在.sbt中可用
  • 方法的具体行为、返回值略有差异

XXXXKey[T]

  • :=:给Setting[T]赋值、计算,并返回Setting[T]

    • SettingKey[T].:=返回Setting[T]
    • TaskKey[T].:=返回Setting[Task[T]]
  • in:获取其他Key的子Key

    1
    sourceDirectories in Compile += Seq(file("1"), file("2"))

SettingKey[T]

  • +=追加单个元素至列表
  • ++=:连接两个列表

String

  • %:从字符串构造Ivy ModuleID对象
  • %%:sbt会在actifact中加上项目的scala版本号
    • 也可手动添加版本号替代
    • 很多依赖会被编译给多个Scala版本,可以确保兼容性
  • at:创建Resolver对象

依赖

非托管依赖

非托管依赖:lib/目录下的jar文件

  • 其中jar被添加进classpath
    • compiletestrunconsole都成立
    • 可通过dependencyClasspath改变设置[某个]classpath

相关Key使用

1
2
3
4
5
6
7
// 定制非托管依赖目录
dependencyClasspath in Compile += <path>
dependencyClasspath in Runtime += <path>
// 定制非托管目录
unmanagedBase := baseDirectory.value / "custom_lib"
// 清空`compile`设置列表
unmanagedJars in Compile := Seq.empty[sbt.Attributed[java.io.File]]

托管依赖

托管依赖:由sbt根据build.sbt中设置自动维护依赖

  • 使用Apache Ivy实现托管依赖
  • 默认使用Maven2仓库

格式

1
2
3
dep_exp ::= <groupID> % <artifactID> % <revision>[% <configuraion>] [% "test" | % Test] [% "provided"]

resolver_exp ::= <name> at <location>
  • groupID
  • acrtifactID:工件名称
  • revision:版本号,无需是固定版本号
    • "latest.integration"
    • "2.9.+"
    • [1.0,)
  • 可选选项
    • "test"|Test:仅在Test配置的classpath中出现
    • "provided":由环境提供,assembly打包时将不打包该 依赖
  • name:指定Maven仓库名称
  • location:服务器地址

依赖添加

1
2
3
4
5
6
7
8
9
10
11
// `sbt.Keys`中依赖声明
val libraryDependencies = settingKey[Seq[ModuleID]]("Delares managed dependencies")

// 添加单个依赖
libraryDependencies += dep_exp
// 添加多个依赖
libraryDependencies ++= Seq(
dep_exp,
dep_exp,
<groupID> %% <artifactID> % <revision>
)

解析器添加

1
2
3
4
5
6
7
8
9
10
11
12
// `sbt.Keys`中解析器声明
val resolvers += settingKeys[Seq[Resolver]]("extra resolvers")

// 添加本地Maven仓库
resolvers += resolver_exp
resolvers += Resolver.mavenLocal
resolvers += "Loal Maven Repo" at "file://" + Path.userHome.absolutePath+"/.m2/repository"

// 将自定义解析器至于默认`externalResolvers`前
externalResolvers := Seq(
resolver_exp
) ++ externalResolvers.values
  • externalResolvers中包含默认解析器,sbt会将此列表值拼接 至resolvers之前,即仅修改resolvers仍然会有限使用默认 解析器

其他配置

project/plugins.sbt

1
2
3
// assembly插件
// `assembly`:将依赖加入jar包,修改jar包配置文件
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.6")

Tmux

Tmux

  • Session:用户与计算计算机的交互

    • 一般而言,temrinal窗口、Session进程绑定
      • 打开窗口,Session开始
      • 关闭窗口,Session结束
  • Tmux:终端复用软件,将terminal窗口、Session解绑

    • 允许多个窗口接入、断开多个Session
      • 新增多个Session
      • 接入已有Session,共享Session
    • 支持窗口竖直、水平拆分

https://man7.org/linux/man-pages/man1/tmux.1.html

命令行参数

  • -S <socket-file>:指定tmux使用socket文件(位置)

Session管理

  • tmux new -s <session-name>:创建指定名称session
    • 缺省数字命名
  • tmux detach:detach当前接入会话
  • tmux ls/list-session:列出session列表
  • tmux rename -t <s1> <s2>:重命名session
  • tmux a [-t <session-name>]:attach指定session
    • 缺省连接上个session
  • tmux switch -t <session-name>:切换至指定session
  • tmux kill-session [[-a] -t s1]:关闭session
    • 缺省关闭上次session
    • -a:关闭除指定session外其他session
  • tmux kill-server:关闭所有session

Tab(Windows)管理

  • tmux new-window [-n <win-name>]:创建新窗口
  • tmux switch-window -t <win-name>:切换到指定窗口
  • tmux rename-window <win-name>:重命名当前窗口

Pane管理

  • tmux split-window [-h]:竖直/水平划分窗口
  • tmux select-pane -U/-D/-L/-R:激活上、下、左、右侧Pane
  • tmux swap-pange -U/-D/-L/-R:当前Pane上、下、左、右 移动

帮助

  • tmux list-key:列出所有绑定键
  • tmux list-command:列出所有命令
  • tmux info:列出当前所有Tmux会话信息
  • tmux source-file <tmux-conf>:重新加载Tmux配置文件

配置

set

  • 默认配置文件为~/.tmux.conf
  • set[-option] [-g] [-a]:session选项

    • 全局、追加标志
      • -g:全局设置
      • -a:追加设置,适合option需要字符串、样式值
    • default-terminal
    • display-time
    • escape-time
    • history-limit
    • base-index
    • pane-base-index
  • setw/set-window-option [-g] [-a]:window选项

    • 全局、追加标志同set[-option]
    • allow-rename
    • mode-keys:快捷键模式,可以设置为vi
    • synchronize-panes
  • set[-option] -s:server选项

StatusBar设置

  • StatusBar主要由5部分组成

    • windows列表
      • windows-status-*:默认windows
      • windows-status-current-*:当前windows
      • windows-status-bell-*:后台有活动windows (需开启支持)
    • 左侧显示区
    • 右侧显示区
    • message显示条:占据整个status bar
    • command输入条:占据整个status bar
  • *-style bg=<color>,fg=<color>,<ATTR>指定样式

    • 颜色可以用名称、colour[0-255]#RGB方式指定
    • 属性包括(前加no表关闭)
      • bright
      • dim
      • underscore
      • blink
      • reverse
      • hidden
      • italics
      • strikethrough
  • *-format:设置格式

    • #{}中变量名称会被替换为相应值,支持alias缩写的变量 可以省略{}
      • host#H
      • host_short#h
      • pane_id#D
      • pane_index#P
      • pane_title#T
      • session_name#S
      • window_flags#F
        • *:当前窗口
        • -:最后打开的窗口
        • z:Zoom窗口
      • window_index#I
      • window_name#W
    • #()会被作为shell命令执行并被替换
      • 命令执行不会阻塞tmux,而是展示最近一次命令执行 结果
      • 刷新频率由status-interval控制
  • 这里介绍2.9之后配置风格

bind

  • bind[-key] [-n] [-r] <key>:key mapping
    • -n:无需prefix
    • -r:此键可能重复
  • unbind <key>:解绑捕获

默认KeyMappings

  • 快捷键前缀缺省为C-b
  • <prefix>::进入命令行
  • 以下快捷键都是缺省值,可以解绑

Session

  • s:列出session,可用于切换
  • $:重命名session
  • d:detach当前session
  • D:detach指定session

Tab/Windows

  • c:创建新tab
  • &:关闭当前tab
  • ,:重命名当前tab
  • .:修改当前tab索引编号
  • w:列出所有tab
  • n/p/l:进入下个/上个/之前操作tab
  • [tab-n]:切换到指定编号窗口
  • f:根据显示内容搜索tab
  • tmux中window相当于tab

Panes

  • %:水平方向创建窗口
  • ":竖直方向创建窗口
  • Up/Down/Left/Right:根据箭头访问切换窗口
  • q:显示窗口编号
  • o:顺时针切换窗口
  • }/{:与下个/上个窗口交换位置
  • space:在预置面板布局中循环切换
    • even-horizontal
    • even-vertical
    • main-horizontal
    • main-vertical
    • tiled
  • !:将当前窗口置于新tab
  • C-o/M-o:顺/逆时针旋转当前窗口,即所有窗口循环前/后 移动一个位次
  • t:在当前窗口显示时间
  • z:最大/恢复当前窗口
  • i:显示当前窗口信息
  • x:关闭当前窗口
  • q:显示当前窗口编号
  • [:进入自由复制模式,VI 模式下快捷键同 VI,且
    • <space>:从光标处开始选择(支持 V 选择行)
    • <enter>:复制选择部分
  • ]:粘贴
  • tmux中pane相当于vim中windows

信息

  • ?:列出所有绑定键

Lagrange 对偶

Langrangian Duality

拉格朗日对偶

  • 考虑优化问题:找到$f(x)$满足约束的最好下界

  • 考虑方程组

    • 方程组无解:$v$是优化问题的一个下界

    • 方程组有解:则可以推出

      • 显然,取$g_1 + g_2 = 0, g_1(x) > 0$是反例,不能 推出原方程有解
    • 由以上方程组有解逆否命题:方程组无解充分条件如下

  • 由此方法推出的最好下界,即拉格朗日对偶问题

说明

  • 拉格朗日对偶对实数域上的优化问题都存在,对目标函数、 约束函数都没有要求

  • 强对偶定理:$v^{} = z^{}$,需要$f,g$满足特定条件才成立

    • 线性规划
    • 半正定规划
    • 凸优化
    • 即需要给约束条件加以限制,使得 是上述方程组有解的冲要条件
  • 弱对偶定理:$v^{} \leq z^{}$,永远成立(以上即可证)

    • 通过弱对偶定理,可以得到原问题的一个下界
    • 对求解原问题有帮助,比如:分支界限法中快速求下界
  • 对偶问题相关算法往往原问题算法在实际应用中往往更加有效

    • dual-simplex
    • primal-dual interior point method
    • augmented Lagrangian Method

原始问题

约束最优化问题

Generalized Lagrange Function

  • 引入Generalized Lagrange Function

    • $x=(x_1, x_2, \cdots, x_n) \in R^n$
    • $\alpha_i \geq 0, \beta_j$:拉格朗日乘子
  • 考虑关于x的函数

    • $P$:primal,原始问题
    • 若x满足原始问题的两组约束条件,则$\theta_P(x)=f(x)$

    • 若x违反等式约束j,取$\beta_j \rightarrow \infty$, 则有$\theta_P(x) \rightarrow \infty$

    • 若x违反不等式约束i,取$\alpha_i \rightarrow \infty$ ,则有$\theta_P(x) \rightarrow \infty$

    则有

  • 则极小化问题,称为广义拉格朗日函数的极小极大问题

    与原始最优化问题等价,两问题最优值相同,记为

对偶问题

  • 定义

  • 再考虑极大化$\theta_D(\alpha, \beta)$,得到广义拉格朗日 函数的极大极小问题,即

    表示为约束最优化问题如下

    称为原始问题的对偶问题,其最优值定义记为

原始、对偶问题关系

定理1

  • 若原始问题、对偶问题都有最优值,则
  • $\forall x, \alpha, \beta$有

  • 而原始、对偶问题均有最优值,所以得证

  • 设$x^{}$、$\alpha^{}, \beta^{}$分别是原始问题、对偶 问题的可行解,且$d^{} = p^{*}$,则其分别是原始问题、 对偶问题的最优解

Python执行模型

综述

Code Blocks

代码块:作为一个单元执行的一段python文本,代码块构成python 程序

  • 模块、函数体、类定义
  • 交互式输入的每条命令
  • 作为标准输入、命令行参数发送给解释器的脚本文件
  • 脚本命令
  • 传递给eval()exec()的字符串参数
  • 代码块在执行帧中执行,执行帧包含某些用于调试的管理信息 并决定代码块执行完成后操作

NamingBinding

Binding of Names

  • 名称:用于指代对象,通过名称绑定操作引入
  • 名称绑定方法

    • 传递参数
    • import语句
      • from ... import *会绑定被导入模块中定义的所有 公有名称(仅在模块层级上被使用)
    • 类、函数定义
    • 以标识符为目标的赋值
    • for循环开头、withexcept子句的as之后

    • del语句的目标也被视为绑定,虽然实际语义为解除名称 绑定

  • 变量类型

    • 局部变量:绑定在代码块中的名称,且未声明为nonlocal 、或global
    • 全局变量:绑定在模块层级的名称
      • 模块代码块中变量既为全局变量、也是局部变量
    • 自由变量:在代码块中使用但未在其中定义的变量
  • 自由变量不同于闭包变量,函数闭包变量__closure__要求 自由变量来自于父函数作用域

Scope

作用域:定义了代码块中名称的可见性

  • 名称的作用域包含

    • 定义该变量代码块
    • 定义变量代码块所包含的代码块 (除非被包含代码块中引入对该名称不同绑定)
  • 名称作用域虽然包括定义变量代码块所包含的代码块

    • 可以直接访问变量
    • 但修改变量必须使用globallocal等声明
  • 在代码块内任何位置进行名称绑定,则代码块内所有对该名称 的使用被认为是对代码块的引用

    • python没有声明语法,则代码块中局部变量可通过,在整个 代码块文本中扫描名称绑定来确定
  • 模块作用域在模块第一次被导入时创建

Resolution of Names

  • 若名称在代码块中被使用,会由包含它的最近作用域来解析

  • 若名称完全无法找到将raise NameError

  • 若当前作用域为函数作用域,且名称指向局部变量,若名称被 绑定值前被被使用将raise UnboundLocalErrorUnboundLocalErrorNameError子类)

  • (代码块)环境:对代码块可见的所作用域集合

global

  • global指定名称的使用是对最高层级命名空间中该名称 绑定的引用

    • 全局命名空间:包含代码块的模块命名空间
    • 内置命名空间:builtins模块的命名空间
  • global语句与同一代码块中名称绑定具有相同作用域

    • 若自由变量最近包含作用域中有global语句,其也会被 当作全局变量
  • global语句须在名称使用之前声明

nonlocal

  • nonlocal使得相应名称指向最近包含函数作用域的中绑定的 变量

  • 指定名称不存在于任何包含函数作用域则raise SyntaxError

内置命名空间

  • 与代码块执行相关联的内置命名空间实际上是通过其在全局命名 空间中搜索名称__builtins__找到,一般是字典、模块 (此时使用模块的命名空间字典)

  • 默认情况下

    • __main__模块中:__builtins__为内置模块builtins
    • __main__模块中:__buitlins__builtins模块 自身的字典别名

动态特性

  • 自由变量的名称解析

    1
    2
    3
    4
    5
    6
    i = 0
    def f():
    print(i)
    i = 42
    f()
    # 打印`42`
    • 发生在运行时,而不是编译时
    • 在全局命名空间中,而不是最近包含命名空间中

eval()exec()

  • eval()exec()没有对完整环境的访问权限来解析名称

    • 有可选参数用于重载全局、局部命名空间
    • 若只指定一个命名空间,则会同时作用于两者

  • 类中名称解析遵守大部分情况下同普通规则,但 未绑定局部变将在全局命名空间中搜索

  • 类定义的命名空间__dict__会成为类的属性字典

  • 类代码块中定义的名称的作用域会被限制在类代码块中,不会 扩展到方法代码块中,包括推导式、生成器表达式

    1
    2
    3
    class A:
    a = 42
    b = list(a + i for i in range(10))

Exception

异常:中断代码块的正常控制流程以便处理错误、其他异常条件的 方式

  • 在错误被检测到的位置引发

    • 解释器检测到运行时错误时错误
    • raise语句显式引发
  • 可被当前包围代码块、任何直接或间接主调代码块捕获、处理

    • try...except指定错误处理,finally子句清理代码
    • 异常通过实例标识、根据器类型选择执行except子句
  • 错误处理采用“终止”模型

    • 异常处理器可以找出发生的问题,在外层继续执行
    • 但不能修复错误的根源,并重试失败操作
    • 异常完全未被处理时,解释器会终止程序执行、或返回交互 模式循环,并打印栈回溯信息,除非为SystemExit异常
  • 异常实例可以携带关于异常状态的额外信息

    • 异常信息不是python API一部分,内容可能在不同python 版本间不经警告的改变

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语句中列出名称不能与之前存在的局部作用域中 绑定冲突

Python 运行时服务

sys

sys:与Python解释器本身相关的组件

平台、版本

1
2
3
4
5
6
7
8
9
10
11
12
import sys
sys.platform
# 操作系统名称
sys.maxsize
# 当前计算机最大容纳“原生”整型
# 一般就是字长
sys.version
# python解释器版本号
sys.byteorder
# 平台字节序
sys.hash_info
# 数值类型hash信息

sys.xxxcheckinterval

1
2
3
4
sys.getcheckinterval()
# 查看解释器检查线程切换、信号处理器等的频率
sys.setcheckinterval(N)
# 设置解释器检查线程切换、信号处理器等的频率
  • 参数

    • N:线程切换前执行指令的数量
  • 对大多数程序无需更改此设置,但是可以用于调试线程性能

    • 较大值表示切换频率较低,切换线程开销降低,但是对事件 的应答能力变弱
    • 较小值表示切换频率较高,切换线程开销增加,对事件应答 能力提升

sys.hash_info

1
2
sys.hash_info.width
# `hash()`函数截取hash值长度

模块搜索路径

1
sys.path
  • 返回值:目录名称字符串组成的列表
    • 每个目录名称代表正在运行python解释器的运行时模块 搜索路径
    • 可以类似普通列表在运行时被修改、生效

sys.path初始化顺序

  • 脚本主目录指示器:空字符串

    • 脚本主目录是指脚本所在目录,不是os.getcwd() 获取的当前工作目录
  • PYTHONPATH环境变量

    1
    2
    # .bashrc
    export PYTHONPATH=$PYTHONPATH:/path/to/fold/contains/module
  • 标准库目录

  • .pth路径文件:在扫描以上目录过程中,遇到.pth文件会 将其中路径加入sys.path

    1
    2
    # extras.pth
    /path/to/fold/contains/module

导入模块顺序

导入模块时,python解释器

  1. 搜索内置模块,即内置模块优先级最高
  2. 从左至右扫描sys.path列表,在列表目录下搜索模块文件

嵌入解释器的钩子

1
2
3
4
5
6
sys.modules
# 已加载模块字典
sys.builtin_module_names
# 可执行程序的内置模块
sys.getrefcount()
# 查看对象引用次数

异常

1
sys.exc_info()
  • 返回值:(type, value, trackback)
    • 最近异常的类型、值、追踪对象元组
    • 处理该异常的except执行之后,sys.exc_info被恢复 为原始值
  • 追踪对象可以使用traceback模块处理

命令行参数

1
sys.argv
  • 返回值:命令行参数列表
    • 首项始终为执行脚本名称,交互式python时为空字符串
  • 参数可以自行解析,也可以使用以下标准库中模块
    • getopt:类似Unix/C同名工具
    • optparse:功能更加强大

标准流

1
2
3
4
5
6
sys.stdin
# 标准输入流
sys.stdout
# 标准输出流
sys.stderr
# 标准错误流
  • 标准流是预先打开的python文件对象

    • 在python启动时自动链接到程序上、绑定至终端
    • shell会将相应流链接到指定数据源:用户标准输入、文件

重定向

  • 可以将sys.stdinsys.stdout重置到文件类的对象,实现 python内部的普遍的重定向方式

    • 外部:cmd输入输出重定向
    • 局部:指定print参数
  • 任何方法上与文件类似的对象都可以充当标准流,与对象类型 无关,只取决于接口

    • 任何提供了类似文件read方法的对象可以指定给 sys.stdin,以从该对象read读取输入

    • 任何提供了类似文件write方法的对象可以指定给 sys.write,将所有标准输出发送至该对象方法上

  • 标准库io提供可以用于重定向的类StringIOByteIO
  • 重定向之后printinput方法将应用在重定向之后的流

stdin

1
2
3
4
5
6
7
8
stdin.read()
# 从标准输入流引用对象读取数据
input("input a word")
sys.stdin.readlines()[-1]
# 以上两行语句类似

stdin.isatty()
# 判断stdin是否连接到终端(是否被重定向)
  • 在stdin被重定向时,若需要接受用户终端输入,需要使用 特殊接口从键盘直接读取用户输入
    • win:msvcrt模块
    • linux:读取/dev/tty设备文件

退出

1
sys.exit(N)
  • 用途:当前线程以状态N退出
    • 实际上只是抛出一个内建的SystemExit异常,可以被正常 捕获
    • 等价于显式raise SystemExit
  • 进程退出参见os._exit()

sys.exitfuncs

1
sys.exitfuncs

编码

1
2
3
4
5
sys.getdefaulencoding()
# 文件内容编码,平台默认值
# 默认输入解码、输出编码方案
sys.getfilesystemencoding()
# 文件名编码,平台默认体系
  • win10中二者都是utf-8,win7中文件名编码是mbcs

sysconfig

builtins

__main__

warnings

dataclass

atexit

atexit:主要用于在程序结束前执行代码

  • 类似于析构,主要做资源清理工作

atexit.register

1
2
3
4
5
def register(
func,
*arg,
**kwargs
)
  • 用途:注册回调函数
    • 在程序退出之前,按照注册顺序反向调用已注册回调函数
    • 如果程序非正常crash、通过os._exit()退出,注册回调 函数不会被调用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import atexit

df func1():
print("atexit func 1, last out")

def func2(name, age):
print("atexit func 2")

atexit.register(func1)
atexit.register(func2, "john", 20)

@atexit.register
def func3():
print("atexit func 3, first out")

实现

atexit内部是通过sys.exitfunc实现的

  • 将注册函数放到列表中,当程序退出时按照先进后出方式 调用注册的回调函数,

  • 若回调函数执行过程中抛出异常,atexit捕获异常然后继续 执行之后回调函数,知道所有回调函数执行完毕再抛出异常

  • 二者同时使用,通过atexit.register注册回调函数可能不会 被正常调用

traceback

traceback.print_tb

1
2
3
4
5
6
7
8
import traceback, sys

try:
...
except:
exc_info = sys.exec_info()
print(exec_info[0], exec_info[1])
traceback.print_tb(exec_info[2])

__future__

gc

inspect

site

abc

ABCMeta

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
from abc import ABCMeta, abstractmethod

class IStream(metaclass=ABCMeta):
@abstractmethod
def read(self, maxbytes=-1):
pass

@abstractmethod
def write(self, data):
pass

class SocketStream(IStream):
# 抽象类目的就是让别的类继承并实现特定抽象方法
def read(self, maxbytes=-1):
pass
def write(self, data):
pass

def serialize(obj, stream):
if not isinstance(stream, IStream):
# 抽象基类的主要用途之一:检查某些类是否为特定类型、
# 实现特定接口
raise TypeError("expect an IStream")
pass

class A(metaclass=ABCMeta):
@property
@abstract
def name(self):
pass

@name.setter
@abstractmethod
def name(self, value):
pass

@classmethod
@abstractmethod
def method1(cls):
pass

@staticmethod
@abstractmethod
def method2():
pass

# `@abstract`还能注解静态方法、类方法、`@property`
# 但是需要保证这个方法**紧靠**函数定义

用途

标准库中有很多用到抽象基类的地方

  • collections模块定义了很多和容器、迭代器(序列、映射、 集合)有关的抽象基类

    1
    2
    3
    4
    5
    6
    import collections as clt

    clt.Sequence
    clt.Iterable
    clt.Sized
    clt.Mapping
  • numbers库定义了跟数据对象:整数、浮点数、有理数有关的 基类

  • IO库定义了很多跟IO操作相关的基类

数据模型--函数决定对象

函数

用户定义函数

用户定义函数:通过函数定义创建,调用时附带参数列表

  • 函数对象支持获取、设置任意属性
    • 用于给函数附加元数据
    • 使用属性点号.获取、设置此类属性

特殊属性

  • __defaults__:有默认值参数的默认值组成元组
    • 没有具有默认值参数则为None
  • __code__编译后函数体代码对象
  • __globals__:存放函数中全局变量的字典的引用
    • 即引用函数所属模块的全局命名空间
    • 只读
  • __closure__:包含函数自由变量绑定单元的元组
    • 没有则为None
    • 只读
  • __annotations__:包含参数注释的字典
    • 字典键为参数名、return(若包含返回值)
    • 将变量名称(非私有)映射到标注值的特殊字典
    • 若该属性可写、在类或模块体开始执行时,若静态地发现 标注则被自动创建
  • __kwdefaults__keyword-only参数的默认值字典
  • 大部分可写属性会检查赋值类型
  • 自由变量:上层命名空间中变量,不包括顶级命名空间,即 全局变量不是自由变量

实例/绑定方法

实例/绑定方法:使用属性表示法调用定义在类命名空间 中的函数

  • 实例方法用于将类、类实例同可调用对象结合起来

    • 可调用对象:须为类属性,即定义在类命名空间中, 通常为用户定义函数、类方法对象
  • 通过实例访问类命名空间定义函数时创建实例/绑定方法

    • 通过示例、类访问的命名空间定义函数类型不同
      • wrapper_descriptor转换为method-wrapper
      • function转换为method
      • 通过类访问方法,得到是普通函数
    • 绑定:此时会将self作为首个参数添加到参数列表
    • 调用实例方法时,调用相应下层函数
  • 函数对象到实例方法对象的转换每次获取实例该属性时都会发生

    • 有时可将属性赋值给本地变量、调用实现性能优化
    • 非用户定义函数、不可调用对象在被获取时不会发生转换
    • 实例属性的用户定义函数不会被转换为绑定方法,仅在函数 是类的属性时才会发生
  • Py3中以上特性依赖于___getattribute__实现

属性

  • 绑定方法对象支持只读获取底层函数对象任意属性

    • 但方法属性实际保存在下层函数对象中
    • 所以不能直接设置绑定方法的方法属性,必须在下层函数 对象中显式设置,否则raise AttributeError
  • 绑定方法有两个特殊只读属性

    • m.__self__:操作该方法的类实例
    • m.__func__:底层实现该方法的函数
  • m(args,...)完全等价于m.__func__(m.__self__,args,...)

特殊元属性

  • __self__:类对象实例
  • __func__:函数对象实例
  • __doc__:方法文档,等同于__func__.__doc__
  • __name__:方法名,等同于__func__.__name__
  • __module__:定义方法所在模块名

Class Method Objects

类方法:提供了始终将类绑定为函数对象首个参数的方式

  • 对其他对象的封装,通常用于封装用户定义方法对象
  • 会改变从类、类实例获取该对象的方式
  • 用途
    • 实现自定义、多个构造器,如
      • 只调用__new__()、绕过__init__,创建未初始化 的实例
      • 反序列化对象:从字节串反序列构造符合要求的对象
  • 通过classmethod()构造器创建
  • Py3中以上特性依赖于___getattribute__实现

Static Method Objects

静态方法:提供了避免将函数对象转换为方法对象的方式

  • 对任意其他对象的封装,通常用于封装用户定义方法对象
  • 从类、类实例获取静态方法对象时,实际返回的是封装的对象, 不会被进一步转换
  • 静态方法对象自身不是可调用的,但其封装的对象通常可调用
  • 通过内置staticmethod()构造器创建
  • Py3中以上特性依赖于___getattribute__实现

内置函数、方法

  • 内置函数:对C函数的外部封装
  • 内置方法:内置函数另一种形式,(类似实例方法)隐式传入 当前实例作为C函数额外参数
  • 包括以下两种类型
    • builtin_function_or_method
    • wrapper_descriptor
  • 参数数量、类型由C函数决定
  • 内置方法由支持其的类型描述

特殊元属性

  • __self__<module 'builtins' (built-in)>
  • __doc__:函数/方法文档
  • __name__:函数/方法名
  • __module__:所在模块名

说明

  • 函数是描述器,函数类function实现有__get__方法
  • function.__get__即将函数首个参数进行绑定,返回绑定方法
  • 参见cs_python/py3ref/cls_special_methods

Generator Functions

生成器函数:使用yield语句的函数、方法称为生成器函数

  • 生成器函数调用时返回生成器迭代器对象,控制执行函数体
  • yield表达式参见cs_python/py3ref/expressions

Generator-Iterator

生成器迭代器:生成器函数执行得到的迭代器

  • 实现有迭代器协议__next__的迭代器类实例不同于 生成器迭代器,其仅实现迭代

    • 迭代执行过程即__next__函数调用,重入(状态维护) 由类负责
    • 无不同迭代状态区别
    • 不会自动获得.send.throw.close等方法
  • 或者说生成器迭代器是:利用yield表达式对迭代器的的简化 实现,并预定义.send.throw.close方法

  • 迭代器协议参见cs_python/py3ref/cls_special_methods

执行过程

  • 其某方法被调用时,生成器函数开始执行
  • 执行到第一个yield表达式被挂起,保留所有局部状态
    • 局部变量当前绑定
    • 指令指针
    • 内部求值栈
    • 任何异常处理状态
  • 返回expression_list
  • 调用生成器某方法,生成函数继续执行
  • 执行return、函数体执行完毕将raise StopIteration
  • 生成器表达式、yield表达式参见 cs_python/py3ref/expressions

生成器迭代器状态

生成器在声明周期中有如下4中状态

  • "GEN_CREATED":等待执行
  • "GEN_RUNNING":正在执行,只有多线程中才能看到
  • "GEN_SUSPENDED":在yield表达式处挂起状态
  • "GEN_CLOSED":关闭状态
  • 可通过inspect.getgeneratorstate()方法查看

__next__

1
2
def(pre) generator.__next__():
pass
  • 用途:开始生成器函数执行、或从上次执行yield表达式处恢复 执行

    • 生成器函数通过__next__方法恢复执行时,yield表达式 始终取值为None
    • 执行至下个yield表达式
  • 返回值:生成器迭代器产生的下个值

    • yield表达式中expression_list
    • 若生成器没有产生下个值就退出,raise StopIteration
  • 此方法通常隐式通过for循环、next()函数调用

send

1
2
def(pre) generator.send(value):
pass
  • 用途:恢复生成器函数执行并向其“发送”值value

    • value参数作为yield表达式的结果
    • 执行至下个yield表达式
  • 返回值:生成器迭代器产生的下个值

    • yield表达式中expression_list
    • 若生成器没有产生下个值就退出,raise StopIteration
  • 说明

    • 若生成器中包含子迭代器,send传参数值将被传递给 下层迭代器,若子迭代器没有合适接收方法、处理,将 raise AttributeErrorraise TypeError
    • .send方法参数为None时实际不会有传递参数行为
      • 调用.send()启动生成器时,必须以None作为调用 参数,因为此时没有可以接收值的yield表达式
      • 子迭代器中没有处理参数时,.send(None)也能正常 工作

throw

1
2
def(pre) generator.throw(type[, value[, traceback]]):
pass
  • 用途:在生成器暂停位置处引发type类型异常

    • 若异常被处理:则执行直至下个yield表达式
    • 若生成器函数没有捕获传入异常、或引发另一个异常:异常 会被传播给调用者
  • 返回值:返回生成器产生的下个值(若异常被处理)

    • 若生成器没有产生下个值就退出,raise StopIteration
  • 说明

    • 若生成器中包含子迭代器,throw传入异常将被传递给 子迭代器
    • 调用throw启动生成器,会在生成器函数开头引发错误

close

1
2
def(pre) generator.close():
pass
  • 用途:在生成器函数暂停处raise GeneratorExit
    • 若之后生成器函数正常退出、关闭、引发GeneratorExit (生成器中未捕获该异常):则关闭生成器并返回调用者
    • 若生成器继续产生值:则raise RuntimeError
    • 若生成器引发其他异常:则传播给调用者
    • 若生成器由于异常、或正常而退出:则无操作

Yield表达式—调用外部函数

  • 生成器函数执行yield表达式类似调用外部函数
  • 当前函数栈保留当前状态、挂起
  • 执行yield表达式“完毕”后,“返回”到调用处
  • yield表达式“返回值”取决于生成器恢复执行所调用方法
    • .__next__fornext()调用,返回None
    • .send():返回.send()参数
  • 此时整体类似于
    • 主调函数调用生成器函数
    • 生成器函数调用yield表达式

生成器函数—协程

  • 生成器函数类似协程,也被称为semi-coroutine,是协程子集
  • 相似点
    • yield多次,有多个入口点
    • 执行可以被挂起
    • 可在恢复执行时传递参数控制执行
  • 不同点
    • yield后控制权总是转移给生成器迭代器调用者,生成器 函数不能控制yield后继续执行的位置 (yield表达式仅仅传递值给父程序,并不是指定需要 跳转的、平等的协程)

生成器函数—try

  • try结构中任何位置都允许yield表达式
  • 若生成器在销毁之前没有恢复执行(引用计数为0、被垃圾 回收),try结构中的yield表达式挂起可能导致finally 子句执行失败

  • 此时应由运行该异步生成器的事件循环、或任务调度器负责调用 生成器-迭代器close()方法,从而允许任何挂起的finally 子句得以执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def echo(value=None):
print("execution start")
try:
while True:
try:
value = (yield value)
except Exception as e:
value = e
finally:
print("clean up when gen.close() is called")

if __name__ == "__main__":
gen = echo(1)
print(next(gen)) # 1
print(next(gen)) # None
print(gen.send(2)) # 2
print(gen.throw(TypeError, "spam")) # TypeError('spam',)
gen.close() # clean up...

Coroutine Function

协程函数:使用async def定义的函数、方法

  • 调用时返回一个coroutine对象
  • 可能包含await表达式、async withasync for语句
    • 即协程函数能在执行过程中挂起、恢复
  • 协程参见cs_program/#todo

Coroutine Objects

协程对象:属于awaitable对象

  • 协程执行:调用__await__,迭代其返回值进行控制

    • 协程结束执行、返回时,迭代器raise StopIteration, 异常的value属性将指向返回值
    • 等待协程超过一次将raise RuntimeError
  • 若协程引发异常,其会被迭代器传播

    • 协程不应该直接引发未处理的StopIteration
  • 可等待对象参见cs_python/py3ref/cls_special_methods

send

1
2
def(pre) coroutine.send(value):
pass
  • 用途:开始、恢复协程执行

    • value is None:相当于等待__await__()返回迭代器 结果下一项
    • value is not None:此方法将委托给导致协程挂起的 迭代器的send()方法
  • 返回值:返回值、异常等同对__await__()返回值迭代结果

throw

1
2
def(pre) coroutine.throw(type[, value[, traceback]]):
pass
  • 用途:在协程内引发指定异常

    • 此方法将委托给导致协程内挂起的迭代器的throw()方法 ,若其存在
    • 否则异常在挂起点被引发
  • 返回值:返回值、异常等同对__await__()返回值迭代结果

    • 若异常未在协程内被捕获,则传回给主调者

close

1
2
def(pre) coroutine.close():
pass
  • 用途:清理自身并退出
    • 若协程被挂起,此方法先被委托给导致该协程挂起的迭代器 的.close()方法,若其存在
    • 然后在挂起点raise GeneratorExit,使得协程立即清理 自身
    • 最后协程被标记为已结束执行,即使未被启动
    • 协程对象将被销毁时,会被自动调用

Asynchronous Generator Functions

异步生成器函数:包含yield语句、使用async def定义函数、 方法(即在协程中)

  • 返回异步生成器迭代器对象,控制执行函数体
  • yield from表达式在异步生成器函数中使用将引发语法错误

Asynchronous Generator-Iterator

异步生成器迭代器

  • 异步体现:async def定义协程函数中可以使用异步代码

  • 异步生成器迭代器方法返回的可等待对象执行同一函数栈

    • 可等待对象被await运行时才会执行异步函数体
    • 类似普通生成器迭代器,执行到yield表达式挂起
    • 则连续返回多个可等待对象可以乱序await
    • 可以视为返回待执行函数体
  • 异步生成器迭代器执行过程、yield表达式、try结构、同异步 迭代器协议关联, 均参见普通生成器迭代器

  • 异步迭代器协议参见cs_python/py3ref/cls_special_methods

  • 异步生成器函数使用yield from语句将引发语法错误

最终化处理#todo

处理最终化:事件循环应该定义终结器函数

  • 其接收异步生成器迭代器、且可能调用aclose()方法并执行 协程
  • 终结器可以通过调用sys.set_asyncgen_hooks()注册
  • 首次迭代时,异步生成器迭代器将保存已注册终结器以便最终化 时调用

__anext__

1
2
async def(pre) coroutine agen.__anext__():
pass
  • 用途:返回可等待对象,可等待对象运行时开始、恢复异步 生成器函数执行

    • 异步生成器函数通过__anext__方法恢复执行时,返回的 可等待对象中yield表达式始终取值为None
  • 可等待对象返回值:返回异步生成器的下个值

    • 执行至下个yield表达式,返回expression_list
    • 若异步生成器没有产生下个值就退出,则 raise StopAsyncIteration
  • 此方法通常由async for异步循环隐式调用

asend

1
2
async def(pre) coroutine agen.asend(value):
pass
  • 用途:返回可等待对象,可等待对象运行时开始、恢复异步 生成器函数执行,并向其“发送”值value

    • value参数作为yield表达式的结果
    • 执行至下个yield表达式
  • 返回值:异步生成器产生的下个值

    • yield表达式中expression_list
    • 若生成器没有产生下个值就退出,则 raise StopAsyncIteration
  • 说明

    • 调用.asend()启动生成器时,必须以None作为调用参数

athrow

1
2
async def(pre) coroutine agen.athrow(type[, value[, traceback]]):
pass
  • 用途:返回可等待对象,可等待对象运行时将在异步生成器函数 暂停位置处引发type类型异常

    • 若异常被处理:则执行直至下个yield表达式
    • 若异步生成器函数没有捕获传入异常、或引发另一个异常: 可等待对象运行时异常会被传播给调用者
  • 返回值:返回生成器产生的下个值(若异常被处理)

    • 若生成器没有产生下个值就退出,则 raise StopAsyncIteration
  • 说明

    • 调用athrow启动异步生成器,会在函数开头引发错误

aclose

1
2
async def(pre) coroutine agen.aclose():
pass
  • 用途:返回可等待对象,可等待对象运行时在异步生成器函数 暂停处raise GeneratorExit
    • 若异步生成器函数正常退出、关闭、引发GeneratorExit (生成器中未捕获该异常):则运行可等待对象将 raise StopIteration???
    • 后续调用异步生成器迭代器方法返回其他可等待对象:则 运行可等待对象将raise StopAsyncIteration
    • 若异步生成器函数产生值:则运行可等待对象将 raise RuntimeError
    • 若异步生成器迭代器已经异常、正常退出,则后续调用 aclose方法将返回无行为可等待对象

数据持久化

DBM

DBM文件:python库中数据库管理的标准工具之一

  • 实现了数据的随机访问
    • 可以使用键访问存储的文本字符串
  • DBM文件有多个实现
    • python标准库中dbm/dbm.py

使用

  • 使用DBM文件和使用内存字典类型非常相似
    • 支持通过键抓取、测试、删除对象

pickle

  • 将内存中的python对象转换为序列化的字节流,可以写入任何 输出流中
  • 根据序列化的字节流重新构建原来内存中的对象
  • 感觉上比较像XML的表示方式,但是是python专用
1
2
3
4
5
6
7
import pickle
dbfile = open("people.pkl", "wb")
pickle.dump(db, dbfile)
dbfile.close()
dbfile = open("people.pkl", "rb")
db = pickle.load(dbfile)
dbfile.close()
  • 不支持pickle序列化的数据类型
    • 套接字

shelves

  • 就像能必须打开着的、存储持久化对象的词典
    • 自动处理内容、文件之间的映射
    • 在程序退出时进行持久化,自动分隔存储记录,只获取、 更新被访问、修改的记录
  • 使用像一堆只存储一条记录的pickle文件
    • 会自动在当前目录下创建许多文件
1
2
3
4
5
6
import shelves
db = shelves.open("people-shelves", writeback=True)
// `writeback`:载入所有数据进内存缓存,关闭时再写回,
// 能避免手动写回,但会消耗内存,关闭变慢
db["bob"] = "Bob"
db.close()

copyreg

marshal

sqlite3

二进制数据服务

struct

struct模块:用于打包、拆包Cstruct格式二进制数据

  • 使用python字节串存储Cstruct数据
  • 需要给出格式声明,指定二进制中存储格式

格式说明

字节序、对齐

格式符 字节序(大、小端) 类型大小 字段对齐方式
@(缺省值) 原生字节序 原生大小 原生对齐
= 原生字节序 标准大小 标准对齐
< lb-endian 标准大小 标准对齐
> bl-endian 标准大小 标准对齐
! >
  • 原生对齐:C对齐方式,字段起始位置须为其长度整数倍
  • 标准对齐:按字节对齐,无需使用0补齐

类型

Format C Type Python Bytes
x pad byte no value 1
? _Bool bool 1
h short integer 2
H unsigned short integer 2
i int integer 4
I unsigned int integer 4
l long integer 4
L unsigned long long 4
q long long long 8
Q unsigned long long long 8
f float float 4
d double float 4
c char str of length1 1
b signed char bytes of length1 1
B unsigned char bytes of length1 1
s char[] str 1
p pascal string,带长度 str NA
n ssize_t integer NA
N size_t integer NA
P void * 足够容纳指针的整形 NA
  • 在类型符前添加数字可以指定类型重复次数

  • 字符、字符串类型实参须以字节串形式给出

    • 字符:必须以长度为1字节串形式给出,重复须对应多参数
    • 字符串:可以是任意长度字节串,重复对应单个字符串
      • 长于格式指定长度被截断
      • 短于格式指定长度用\0x00补齐
  • python按以上长度封装各类型,但C各类型长度取决于平台, 以上近视64位机器最可能对应C类型

    • 非64位机器long long类型大部分为4bytes
  • 用途

    • 封装、解压数据
    • reinterpret_cast类型转换

Struct

1
2
3
class Struct:
def __init__(self, fmt):
pass
  • 用途:根据指定格式fmt编译Sturct对象
    • Sturct对象可以调用以下方法,无需再次指定fmt

Pack

1
2
3
4
def struct.pack(fmt, v1, v2,...) -> bytes:
pass
def struct.pack_into(fmt, buffer, offset , v1, v2, ...):
pass
  • pack:按照指定格式fmt封装数据为字节串
  • pack_into:将字节串写入buffer指定偏移offset

Unpack

1
2
3
4
5
6
def struct.unpack(fmt, buffer) -> (v1, v2,...):
pass
def struct.unpack(fmt, buffer, offset=0) -> (v1, v2, ...):
pass
def struct.iter_unpack(fmt, buffer) -> iterator(v1, v2,...):
pass
  • unpack:按照指定格式fmt拆包字节串buffer
  • unpack_from:从偏移offset处开始拆包
  • iter_unpack:迭代解压包含多个fmtbuffer

calsize

1
2
def struct.calsize(fmt) -> integer:
pass
  • 用途:计算封装指定格式fmt所需字节数

codecs

网络、进程间通信

asyncio

协程与任务

  • 协程包含两层概念

    • 协程函数:定义形式为async def的函数
    • 协程对象:调用协程函数返回的对象
  • 可等待对象:可在await语句种使用的对象

    • 协程对象
    • asyncio.Future:异步调用结果的占位符
      • 以便通过async/await使用基于回调的代码
      • 通过情况下无需在应用层级代码中显式创建Future 对象
    • asyncio.TaskFuture子类,包装coroutine的future
  • 运行协程(对象)的三种方式

    • await阻塞式等待协程执行完毕
      • 只能在async def函数中使用
      • await同样是在事件循环中阻塞执行
    • 将协程对象包装为可并发运行的asyncio.Task,并在事件 循环中并发执行
      • asyncio.create_task
    • asyncio.run()创建、管理事件循环的高层API
      • 启动事件循环执行是真正运行协程对象的开始

asyncio.run

1
def asyncio.run(coro, *, debug=False);
  • 功能:创建新的事件循环并在结束时关闭

    • 执行传入的协程,并返回结果
    • 管理asyncio事件循环、终结异步生成器、关闭线程池
  • 应当被用作asyncio程序的主入口点,理想情况下只被调用一次

  • 同一线程中只能有一个asyncio事件循环运行,若同线程中有 其他事件循环运行,此函数不能被调用

Task

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class asyncio.Task(Future):
def __init__(coro,*,loop=None,name=None);
# 1、此方法仅在下轮事件循环中`raise asyncio.CancelledError`
# 给被封包协程,协程自行选择是否取消
# 2、协程等待的`Future`对象同样会被取消
def cancel(msg=None);
bool cancelled();
bool done();
def result();
def exception();
def add_done_callback(callback, *, context=None);
def remove_done_callback(callback);
def get_stack(*, limit=None);
def print_stack(*, limit=None, file=None);
def get_coro();
def get_name();
def set_name(value);
  • Task:用于在事件循环中运行协程,非线程安全
    • 若协程在等待Future对象,Task对象会挂起该协程执行 并等待该Future对象完成再执行
    • 事件循环使用协同日程调度,事件循环每次运行一个Task 对象,Task对象会等待Future对象完成,事件循环会 运行其他Task、回调、执行IO操作
    • 不建议手动实例化Task对象,可以使用高层级的 asyncio.create_task(),或低层级的 loop.create_task()ensure_future()创建
  • asyncio.TaskFuture继承了除Future.set_result()Future.set_exception()外的所有API

create_task

1
def asyncio.create_task(coro, *, name=None);
  • 功能:将协程打包为task,排入日程准备执行

  • 任务会在get_running_loop()返回的循环中执行

    • 若线程中没有在运行的循环则引发RuntimeError
  • python3.7加入,之前版本可以使用asyncio.ensure_future()

gather

1
awaitable asyncio.gather(*aws, return_exception=False)
  • 功能:并发运行aws序列中的可等待对象

    • aws中的某个可等待对象为协程对象,则会自动作为 任务加入日程
    • 若所有等待对象都成功完成,结果将是所有返回值列表, 结果顺序同aws中对象顺序
    • gather被取消,被提交的可等待对象也被取消
    • aws中task、future被取消,将被当作引发 CancelledError处理,gather也不会被取消
  • 参数说明

    • return_exception
      • False:首个异常被传播给等待gather()的任务
      • True:异常和成功结果一样处理并被聚合至结果列表

shield

1
awaitable asyncio.shield(aw);
  • 功能:保护可等待对象防止其被取消

    • aw是协程,则将自动作为任务加入日程
    • 包含shield的协程被取消,aw中的任务不会被取消, 但若aw的调用者被取消,await表达式仍然会 raise CancelledError
    • 若通过其他方式取消aw,则shield也会被取消
  • 希望完全忽略取消操作则需要配合try/except

    1
    2
    3
    4
    try:
    res = await shield(aw)
    except CancelledError:
    res = None

其他

  • Task内省

    1
    2
    3
    4
     # 返回当前运行Task实例,没有则返回`None`
    Task = asyncio.current_task(loop=None)
    # 返回`loop`事件循环未完成Task对象
    Set(Task) = asyncio.current_task(loop=None)
  • Sleep

    1
    coroutine asyncio.sleep(delay, result=None, *, loop=None)

等待超时

wait_for

1
coroutine asyncio.wait_for(aw, timeout);
  • 功能:等待aw可等待对象完成
    • 发生超时则取消任务并raise asyncio.TimeoutError
    • 函数会等待直到aw实际被取消,则总等待时间可能会超过 timeout
    • 可以通过shield避免任务被取消
    • 若等待被取消,则aw也被取消

wait

1
2
(done, pending) asyncio.wait(aws, *, timeout=None,
return_when=ALL_COMPELTED);
  • 功能:并发运行aws并阻塞线程直到满足return_when指定 的条件

    • 超时不会raise asyncio.TimeoutError,而会在返回未 完成的FutureTask
  • 参数

    • return_when
      • FIRST_COMPLETED:任意可等待对象结束、取消时 返回
      • ALL_COMPLETED:所有可等待对象结束、取消时返回
      • FIRST_EXCEPTION:任意可等待对象引发异常结束时 返回,否则同ALL_COMPLETED

as_completed

1
iterator asyncio.as_completed(aws, timeout=None);
  • 功能:并发运行aws中可等待对象,返回协程迭代器,返回的 每个协程可被等待以从剩余可等待对象集合中获得最早下个结果
    • 超时则raise asyncio.TimeoutError
1
2
for coro in asyncio.as_completed(aws):
earliest_result = await coro

其他线程中执行

to_thread

1
coroutine asyncio.to_thread(func, *args, **kwargs);
  • 功能:在不同线程中异步运行函数func
    • argskwargs会被直接传给func
    • 当前contextvars.Context被传播,允许在不同线程中 访问来自事件循环的上下文变量
    • 主要用于执行可能会阻塞事件循环的函数
      • 对于CPython实现,由于GIL存在,此函数一般只能将 IO密集型函数变为非阻塞
      • 对于会释放GIL的扩展模块、无此限制的替代性python 实现,此函数也可以被用于CPU密集型函数

run_coroutine_threadsafe

1
concurrent.futures.Future asyncio.run_coroutine_threadsafe(coro, loop)
  • 功能:向事件循环loop提交协程coro
    • 线程安全
    • 此函数应该从另一个系统线程中调用

socket

ssl

select

selectors

asyncore

asynchat

signal

mmap