架构相关算法

无符号整形二进制

1数量

  • 目标:统计 unsinged 二进制表示中 1 数量
  • 思路方向
    • 移位遍历
    • 分治统计

仅遍历1

1
2
3
4
5
6
7
8
int population(unsigned int bits){
int result = 0;
while (bits != 0){
bits &= bits - 1;
++result;
}
return result;
}
  • 遍历减少:仅仅遍历无符号整形二进制表示中 1 次数
    • bits &= bits - 1 将末尾 1 消除

分治+查表

1
2
3
4
5
6
7
8
9
10
11
12
13
int * initialization(){
int * table = new int[256];
for (int i = 1; i < 256; i++){
table[i] = (i & 0x1) + table[i >> 1];
}
return table;
}
int population(int bits, int * table){
return table[bits & 0xff] +
table[(bit >> 8) & 0xff] +
table[(bit >> 16) & 0xff] +
table[(bit >> 24) & 0xff]
}
  • 思路
    • 建表:为 8bits 数建立 256 长度的 1 数量表
      • 递推建立:f(n) = f(n >> 1) + last_bit(n)
    • 分治:将无符号整型分4块查表、加和结果

分治

1
2
3
4
5
6
7
8
9
int population(unsigned int bits){
// 分组计算
bits = (bits & 0x55555555) + ((bits >> 1) & 0x55555555);
bits = (bits & 0x33333333) + ((bits >> 2) & 0x33333333);
bits = (bits & 0x0f0f0f0f) + ((bits >> 4) & 0x0f0f0f0f);
bits = (bits & 0x00ff00ff) + ((bits >> 8) & 0x00ff00ff);
bits = (bits & 0x0000ffff) + ((bits >> 16) & 0x0000ffff);
return bits;
}

unsigned_population__division

  • 分治统计:依次将相邻 2bits、 4bits 分组,计算组内 1数量
    • 移位、求并:将组内无效 bits 置 0、并对齐
    • +:计算组内 1 数量
      • 0x0 + 0x0 = 0x0
      • 0x0 + 0x1 = 0x1
      • 0x1 + 0x1 = 0x10:进位,符合逻辑
  • 改进方式:考虑避免不必要的 &
    • 根据进位特性替换 2bits 组对应语句
    • 考虑组内空位是否可容纳 + 后结果
      • 8bits 组:有效半组 4bits 足够存储中的最大 1 数量 8,但 无法存储 16 或更大, 需要及时置空无效bit
      • 16bits 组及之后:有效半组足够存储最大 1 数量 32,可以 计算完之后再取值
1
2
3
4
5
6
7
8
9
10
11
int population(unsigned int bits){
// 等于上述首行
bits = bits - ((bits >> 1) & 0x55555555);
bits = (bits & 0x33333333) + ((bits >> 2) & 0x33333333);
// 4bits 足够存储组内 8bits 中 `1` 最大数量 8
bits = (bits + (bits >> 4)) & 0x0f0f0f0f;
// 8bits 足够存储全部 32bits 中 `1` 最大数量 32
bits = bits + (bits >> 8);
bits = bits + (bits >> 16);
return bits & 0x3f
}

奇偶性

  • 目标:判断 unsigned 二进制表示中 1 数量奇偶性
  • 思路方向
    • 移位遍历,注意语言特性
      • 逻辑移位和算术移位
      • 求余结果
    • 分治统计

分治统计

1
2
3
4
5
6
7
8
9
10
11
12
// 原始版本
unsigned char parity(unsigned int i){
// 相邻2bit一组异或确定奇偶
i = i ^ (i >> 1);
// 相邻4bit一组,依赖以上结果确定奇偶
i = i ^ (i >> 2);
i = i ^ (i >> 4);
i = i ^ (i >> 8);
i = i ^ (i >> 16);
// 奇偶性保存在最后1bit,取出
return i & 0x1;
}
  • 分治统计:依次将相邻 2bits、 4bits 分组,统计组内奇偶性
    • 分组方式顺序调换仅影响中间结果中存放奇偶性统计结果 bits 位置
      • 奇偶性统计结果存放在组内最后 bit
      • 其中每次分组统计事实上都是统计 2bits 的奇偶性
  • 改进方式
    • 调整分组顺序,将存储奇偶性 bits 位置移至最后
    • 计算奇偶性 bits 对应奇偶性表,查表得到结果
      • 一般可以设置长度为 0x0f 长度的数组,其中取值为索引奇偶性
      • 0x6996 即为对应奇偶性表,其中各位序 bit 取值为位序值对应 的奇偶性
1
2
3
4
5
6
7
8
9
// 改进版本
unsigned char parity_k(unsigned int i){
// 将存储奇偶性 bits 移至最后
i = i ^ (i >> 4);
i = i ^ (i >> 8);
i = i ^ (i >> 16);
// 查表得到结果
return (0x6996 >> (i & 0x0f )) & 0x01;
}

奇偶性填充

  • 目标:将 unsigned char 中最高位作为校验位,保证整体的二进制标表示的奇偶性
  • 思路方向
    • 1数量并设置标志位
    • 按位乘积后取模

取模

1
2
3
4
5
6
unsigned char even(unsigned char i){
return ((i * 0x10204081) & 0x888888ff) % 1920;
}
unsigned char odd(unsigned char i){
return ((i * 0x00204081) | 0x3DB6DB00) % 1152;
}
  • 各数字二进制含义(设 i 的二进制表示为 abcdefg
    • 0x10204081 * i 得到 i 二进制重复 5 次(溢出被截断)
    • 0x888888ff & 抽取所需 bits d000a000e000b000f000c000gabcdefg
    • 1920 = 15 * 128:对其取模即得到[X]abcdefg (将被除数表示为 16 进制分块可证)

位元反序

  • 目标:返回 6bits 长二进制的反序
1
2
3
unsigned char revert(unsigned char i){
return ((i * 0x00082082) & 0x01122408) % 255;
}
  • 各数字二进制含义(设 i 的二进制表示为 abcdef
    • 0x00082082 * i 得到 i 二进制重复 4 次
    • 0x01122408 & 抽取所需 bits 0000000a000e00b000f00c000000d000
    • 255 取模即得到反序*(将被除数表示为 256 进制分块可证)

前导 0

  • 目标:获取 unsigned 的二进制表示中前导 0 数量
  • 思路方向
    • 移位遍历
    • 区间映射:将原始 unsigned 映射为较小范围的取值

区间映射

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
unsigned char nlz(unsigned int i){
static unsigned char table[64] = {
32, 31, 'u', 16, 'u', 30, 3, 'u', 15, 'u', 'u', 'u', 29,
10, 2, 'u', 'u', 'u', 12, 14, 21, 'u', 19, 'u', 'u', 28,
'u', 25, 'u', 9, 1, 'u', 17, 'u', 4, 'u', 'u', 'u', 11,
'u', 13, 22, 20, 'u', 26, 'u', 'u', 18, 5, 'u', 'u', 23,
'u', 27, 'u', 6, 'u', 24, 7, 'u', 8, 'u', 0, 'u'
}

i = i | (i >> 1);
i = i | (i >> 2);
i = i | (i >> 4);
i = i | (i >> 8);
i = i | (i >> 16);
i = i * 0x06eb14f9;
return table[i >> 26];
}
  • 区间映射
    • 移位取或:将最高位 1 传播至所有低位,原始值映射至 33 种取值
    • 0x06eb14f9:将 33 种值映射为低 6 位取值均不同值
      • 此类数的共同特点是因子均为 $2^k \pm 1$ (此类数乘法容易通过移位操作实现)
      • 最小的此性质的数为 0x45bced1 = 17 * 65 * 129 * 513

速算

无符号整形除法

  • 目标:大部分计算机架构中除法耗时,寻找方法将特定除数的除法转换为 其他指令
  • 思路:除数为常数时,用移位、乘法、加法替代除法运算
    • 常数除法(有符号或无符号)基本都有对应的乘法版本
    • 注意类型溢出

除数为3

1
2
3
4
5
unsigned div3(unsigned int i){
// 在更高级别优化过程实际就会转化为类似指令
// 但此语句可能会导致类型溢出
return (i * 2863311531) >> 33;
}

快速求平方根倒数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
float i_sqrt(float a){
union {
int ii;
float i;
};
i = a;
float ihalf = 0.5f * i;

// 得到较好的初始估算值
ii = 0x5f000000 - (ii >> 1);

// 牛顿法迭代,可以重复以下语句多次提高精度
i = i * (1.5f - ihalf * i * i);

return i;
}
  • 移位获取初始值

    • 考虑(规格化)单精度浮点数 $a$ 的二进制表示

    • 0x5f000000 - (ii >> 1) 可以满足指数部分得到近似结果 $190 - \frac E 2$

    • 其他细节使得存在比 0x5f000000 实践更优值,如:0x5f375a86
      • 规格化值的底数接近 1
      • 移位运算指数最后位部分移至尾数
      • 减法运算尾数部分向指数借位
  • 牛顿法:$x{n+1} = xn - \frac {f(x_n)} {f^{‘}(x__n)}$

    • 求 $\frac 1 {\sqrt x}$,即求 $f(x) = x^{-2} - a$ 零点
    • 则迭代为 $x{n+1} = xn(1.5 - 0.5 a x__n^2)$

文件、目录访问

pathlib

os.path

判断存在

1
2
3
4
5
os.path.isdir(r"C:\Users")
os.path.isfile(r"C:\Users")
# 判断路径名是简单文件、目录
os.path.exists(r"C:\Users")
# 判断路径名是否存在
  • os.stat配合stat模块有更丰富的功能

路径操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
pfile = os.path.join(r"C:\temp", "output.txt")
# 连接文件名、目录

os.path.split(pfile)
# 分离文件名、目录

os.path.dirname(pfile)
# 返回路径中目录
os.path.basename(pfile)
# 返回路径中
os.path.splitext(pfile)
# 返回文件扩展名

os.path.normpath(r"C:\temp/index.html")
# 调整路径为当前平台标准,尤其是分隔符混用时
os.path.abspath("index.html")
# 返回文件的**完整绝对路径名**
# 扩展`.`、`..`等语法
  • os.sep配合字符串.join.split方法可以实现基本相同 效果

fileinput

stat

stat:包含os.stat信息相关常量、函数以便跨平台使用

1
2
3
4
5
6
7
8
import stat

info = os.stat(filename)
info[stat.ST_MODE]
# `stat.ST_MODE`就是字符串
# 只是这样封装易于跨平台
stat.S_ISDIR(info.st_mode)
# 通过整数`info.st_mode`判断是否是目录
  • os.path中包含常用部分相同功能函数

glob

glob.glob

1
2
3
import glob

def glob.glob(pathname,*,recursive=False)
  • 参数

    • pathname:文件名模式
      • 接受shell常用文件名模式语法
        • ?:单个字符
        • *:任意字符
        • []:字符选集
      • .开头路径不被以上?*匹配
    • recursive
      • False:默认
      • True**将递归匹配所有子目录、文件
  • 返回值:匹配文件名列表

    • 目录前缀层次同参数
  • glob.glob是利用glob.fnmatch模块匹配名称模式

shutil

shutil模块:包含文件操作相关

fnmatch

linecache

macpath

filecmp

tempfile

通用操作系统服务

os

os:与Python所在底层操作系统相对应变量、函数

  • os模块提供了POSIX工具

    • 操作系统调用的跨平台移至标准
    • 不依赖平台的目录处理工具
      • os.path
  • 包含在C程序、shell脚本中经常用到的所有操作系统调用,涉及 目录、进程、shell变量

  • 实践中,os基本可以作为计算机系统调用的可移植接口 使用

    • 只要技术上可行,os模块都能跨平台
    • 但在某些平台,os提供专属该平台的工具

Shell变量

1
2
3
4
5
os.environ
# 获取、设置shell环境变量,类似字典
os.putenv()
# 修改进程对应shell环境变量
os.getenv()

os.environ

os.environ可以向普通字典一样键索引、赋值

  • 默认继承系统所有环境变量、命令行临时环境变量

  • 在最新的python中,对os.environ的键值修改将自动导出 到应用的其他部分

    • os.environ对象
    • 进程对应shell环境变量:通过后台调用os.putenv生效, 反之不会更新os.environ
  • python进程、链入C模块、该进程派生子进程都可以获取新的 赋值

    • 子进程一般会继承父进程的环境变量设定
    • 可以作为传递信息的方式

os.putenv

  • os.putenv同时会调用C库中的putenv(若在系统中可用) 导出设置到python链接的C库

    • 底层C库没有putenv则可将os.environ作为参数传递

管理工具

1
2
3
4
5
6
os.getpid()
# 调用函数的进程id
os.getcwd()
# 当前工作目录CWD
os.chdir(r"C:\Users")
# 更改当前工作目录CWD

移植工具

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
os.sep
# python底层运行平台采用的**目录组**分隔符号
# linux: `/`、win:`\`、某些mac:`:`
os.pathsep
# 目录列表(字符串形式)中分隔目录的字符
# posix机:`:`、win:`;`
os.curdir
# 当前目录代表
# linux:`.`
os.pardir
# 父目录代表
# linux:`..`
os.linesep
# 换行符
# linux:`\n`

||Linux|Win|Unix| |———|——————|———|———| |sep|/|\|/(某些MAC:)| |pathsep|:|;|| |curdir|.||| |pardir|..||| |linesep|\n|\r\n||

  • 借助这些变量可以系统相关字符串操作的跨平台
  • win下目录组分隔符是\,大部分情况下看到\\是作为\ 转义字符,防止\和之后字符转义
    • 确认不会转义时,直接使用\也是可以的
    • 使用r''表示不转义也可以直接使用\

目录、文件操作

1
2
3
4
5
6
7
8
9
10
11
12
os.mkdir(dirname)
os.rename(ori_name, dest_name)
os.remove(filename)
os.unlink(filename)
# unix下文件删除,同`os.remove`
os.chmod(filename, previlideges)
info = os.stat(filename)
# 命名元组表示的文件底层信息
# 可使用`stat`模块处理、解释信息
os.listdir(dirpath)
os.walk(rootdir, topdown=True/False)
# 遍历根目录下的整个目录树

os.listdir

  • 返回值:包含目录中所有条目名称的列表

    • 名称不带目录路径前缀
  • 需要注意的是:文件名同样有编码

    • 若参数为字节串,返回文件名列表也是字节串
    • 参数为字符串,返回文件名列表也是字符串
    • open函数也可以类似使用字节串确定需要打开的文件
    • glob.globos.walk内部都是通过调用os.listdir 实现,行为相同
  • glob模块也有遍历目录的能力

os.walk

  • 返回值:返回迭代器

    • 每个元素为(dirname, subdirs, subfile)
  • 参数

    • topdown:默认True,自顶向下返回

文件描述符、文件锁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
descriptor = os.open(path, flags, mode)
# 打开文件并返回底层描述符
os.read(descriptor, N)
# 最多读取N个字节,返回一个字节串
os.write(descriptor, string)
# 将字节串写入文件
os.lseek(descriptor, position, how)
# 移动文件游标位置
descriptor.flush()
# 强制刷出缓冲

new_fd = os.dup(fd)
# 创建文件描述符副本
os.dup2(fd_src, fd_dest)
# 将文件描述符`fd_src`复制至`fd_dest`
  • os通过调用文件的描述符来处理文件

  • 基于文件描述符的文件以字节流形式处理

    • 没有字符解码、编码、换行符转换
    • 除了缓冲等额外性能,基于描述符的文件和二进制文件模式 对象类似
  • 文件流对象、工具仅仅是在基于描述符的文件的封装

    • 可以通过.fileno()获得文件流对象对应文件描述符, sys.stdinsys.stdoutsys.stderr对应文件 描述符是:0、1、2

      1
      2
      os.write(1, b"hello world\n")
      sys.stdout.write("hello world\n")
    • 可以通过os.fdopen把文件描述符封装进文件流对象

      1
      2
      3
      fdfile = os.open("filename", (os.O_RDWR|os.O_BINARY))
      filstream = os.fdopen(fdfile, "r", encoding="utf-8",
      closefd=False)

os.open

1
2
3
4
5
def os.open(path,
flags,
mode=511, *,
dir_fd=None
)
  • 参数

    • mode:需要模式标识符进行二进制操作以得到需要的模式

      • os.O_RDWR
      • os.O_RDONLY
      • os.O_WRONLY
      • os.O_BINARY
      • os.O_EXCL:唯一访问权,是python在并发、进程 同步情况下锁定文件最便捷的方法
      • os.O_NONBLOCK:非阻塞访问
      • 其他模式选项参见os模块
  • 返回值:文件描述符

    • 整数代码、句柄,代表操作系统的中文件

退出进程

1
2
os._exit(0)
# 调用进程立即退出,不输出流缓冲、不运行清理处理器

io

time

argparse

argparse:编写用户友好的命令行接口

argparse.ArgumentParser

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
class argparse.ArgumentParser:
def __init__(self,
prog=None, # 程序名,可用`$(prog)s`引用
usage=None, # 用法字符串
description=None, # 程序描述
epilog=None, # 程序尾描述
parents=[], # 父解析器,共用当前参数
formatter_class=argparse.HelpFormatter,
prefix_chars="-"/str, # 参数前缀(可包含多个可选)
fromfile_prefix_chars=None, # 指定其后参数为存储参数文件名
argument_default=None,
conflict_handler="error"/"resolve" # 默认不允许相同选项字符串
# 有不同行为,可设置为
# "resolve"表示允许覆盖
add_help=True, # 符解释器中应禁止
allow_abbrev=True, # 允许前缀匹配(若不存在重复前缀)
):
pass

# 打印、输出信息至标准输出
def print_usage(self, file=None/IO) -> None:
pass
def print_help(self, file=None/IO) -> None:
pass
def format_usage(self) -> str:
pass
def format_help(self) -> str:
pass

# 退出
def exit(self, exit=0, message=None):
pass
def error(self, message):
pass

添加参数

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
 # 添加参数
def add_argument(self,
?*name_or_flag=*[str], # 选项名(无前缀表示位置参数)
action="store"/"store_const"/ # 参数动作
"store_true"/"store_false"/
"append"/"append_const"/
"count"/"help"/"version",
nargs=None/int/"?"/"*"/"+"
const=None, # `action`、`nargs`所需常数
default=None, # 未指定参数默认值
type=str/type, # 参数被转换类型、内建函数
choices=None, # 参数类型
required=None, # 选项候选集
help=None, # 选项描述
metavar=None, # 参数值示例
dest=None, # `parse_args()`返回的参数属性名
):
pass

# 添加参数组,参数选项将可以注册至此
def add_argument_group(self,
title=None,
description=None
) -> argparse._ArgumentGroup:
pass

# 添加互斥参数组,互斥组内仅有一个参数可用
def add_mutally_exclusive_group(self,
rquired=False # 互斥组中至少有一个参数
) -> argparse._MutaullyExclusiveGroup:
pass

# 设置默认值,优先级高于选项中默认值
def set_defaults(self,
**kwargs: {参数属性名: 默认值}
):
pass

# 获取默认值
def get_default(self,
**kwargs: {参数属性名: 默认值}
):
pass
  • 参数
    • action:关联命令行参数、动作,除以下预定义行为, 还可以传递argparse.Action子类、相同接口类
      • store:存储值,默认行为
      • store_const:存储const指定值,通常用于在 选项中指定标志
      • store_true/"store_false":类上
      • append:存储列表,适合多次使用选项
      • append_const:将const参数值追加至列表, 适合多个选项需要在同一列表中存储常数 (即多个dest参数相同)
      • count:计算选项出现次数
      • help:打印完整帮助信息
      • version:打印version参数值
-    `nargs`:参数消耗数目,指定后`pare_args`返回列表,
    否则参数消耗由`action`决定

    -    `int`:消耗参数数目,`nargs=1`产生单元素列表,
        和默认不同
    -    `?/*/+`:类似普通正则,`+`会在没有至少一个参数时
        报错
    -    `argparse.REMAINDER`:所有剩余参数,适合用于从
        命令行传递参数至另一命令行

-    `const`:保存不从命令行读取、被各种动作需求的常数
    -    `action="store_const"/"append_const"`:必须给出
    -    `nargs=?`:气候选项没有参数时,使用`const`替代

-    `type`:允许任何类型检查、类型转换,一般内建类型、
    函数可以直接使用
    -    `argparse.FiltType("w")`:为文件读写方便,预定义
        类型转换

-    `dest`:`parse_args()`返回的参数属性名
    -    位置选项:缺省为首个选项名
    -    关键字选项:优秀首个`--`开头长选项名,选项目中间
        `-`被替换为`_`

参数解析

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
 # 解析参数,无法解析则报错
def parse_args(self,
args=None/list, # 需要解析参数列表,默认`sys.argv`
namespace=None # 存放属性的`Namespace`对象,缺省创建新空对象
) -> argparse.Namespace:
pass

# 解析部分参数,无法解析则返回
def parse_known_args(self,
args=None/list,
namespace=None
) -> (argparse.Namespace, [ ]):
pass

# 允许混合位置参数、关键字参数
def parse_intermixed_args(self,
args=None,
namespace=None
) -> argparse.Namespace:
pass
def parse_known_intermixed_args(self,
args=None,
namespace=None
) -> argparse.Namespace:
pass
  • 说明
    • 仅不包含类似负数的选项名,参数才会被尝试解释为负数 位置参数
    • 前缀无歧义时,可以前缀匹配

添加子命令

1
2
3
4
5
6
7
8
9
10
11
12
13
def add_subparsers(self,
title,
description,
prog,
parser_class,
action,
option_string,
dest,
required,
help,
metavar
):
pass

辅助类

动作类

1
2
3
4
5
6
class argparse.Action:
def __init__(self, option_string, dest, nargs=None, **kwargs):
pass

def __call__(self, parser, namespace, values, option_string=None):
pass

格式化类

1
2
3
4
5
6
7
class argparse.ArgumentDefaultHelpFormatter
# 认为两程序描述已被正确格式化,保留原样输出
class argparse.RawDescriptionHelpFormatter
# 保留所有种类文字的空格,包括参数描述
class argparse.RawTextHelpFormatter
# 为每个参数使用`type`参数名作为其显示名,而不是`dest`
class argparse.MetavarTypeHelpFormatter

其他类

1
2
3
4
5
6
7
8
9
10
11
12
 # 容纳属性的类
class argparse.Namespace:
pass
# IO接口简化类
class argparse.FileType:
def __init__(self,
mode="r"/"w",
bufsize=-1,
encoding=None,
errors=None
):
pass

getopt

optparse

logging

logging.config

logging.handlers

getpass

curses

curses.textpad

curses.ascii

curses.panel

platform

errno

ctypes

Python系统编程

综述

主要标准模块

  • os:与Python所在底层操作系统相对应变量、函数

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

  • 文件、目录

    • glob:文件名扩展
    • stat:文件信息
  • 并行开发

    • threading_threadqueue:运行、同步并发线程
    • subprocessmultiprocessing:启动、控制并行进程
    • socket:网络连接、进程间通信
  • 系统

    • timetimeit:获取系统时间等相关细节
    • signalselectshutiltempfile:多种系统 相关任务

说明

  • Python中大部分系统级接口都集中在模块sysos

  • 以上模块之间具体实现不总是遵循规则

    • 标准输入、输出流位于sys中,但可以将视为与操作系统 模式相关
  • 一些内建函数实际上也是系统接口

    • open

Sys模块

平台、版本

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

os模块

  • os模块提供了POSIX工具

    • 操作系统调用的跨平台移至标准
    • 不依赖平台的目录处理工具
      • os.path
  • 包含在C程序、shell脚本中经常用到的所有操作系统调用,涉及 目录、进程、shell变量

  • 实践中,os基本可以作为计算机系统调用的可移植接口 使用

    • 只要技术上可行,os模块都能跨平台
    • 但在某些平台,os提供专属该平台的工具

Shell变量

1
2
3
4
5
os.environ
# 获取、设置shell环境变量,类似字典
os.putenv()
# 修改进程对应shell环境变量
os.getenv()

os.environ

os.environ可以向普通字典一样键索引、赋值

  • 默认继承系统所有环境变量、命令行临时环境变量

  • 在最新的python中,对os.environ的键值修改将自动导出 到应用的其他部分

    • os.environ对象
    • 进程对应shell环境变量:通过后台调用os.putenv生效, 反之不会更新os.environ
  • python进程、链入C模块、该进程派生子进程都可以获取新的 赋值

    • 子进程一般会继承父进程的环境变量设定
    • 可以作为传递信息的方式

os.putenv

  • os.putenv同时会调用C库中的putenv(若在系统中可用) 导出设置到python链接的C库

    • 底层C库没有putenv则可将os.environ作为参数传递

管理工具

1
2
3
4
5
6
os.getpid()
# 调用函数的进程id
os.getcwd()
# 当前工作目录CWD
os.chdir(r"C:\Users")
# 更改当前工作目录CWD

移植工具

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
os.sep
# python底层运行平台采用的**目录组**分隔符号
# linux: `/`、win:`\`、某些mac:`:`
os.pathsep
# 目录列表(字符串形式)中分隔目录的字符
# posix机:`:`、win:`;`
os.curdir
# 当前目录代表
# linux:`.`
os.pardir
# 父目录代表
# linux:`..`
os.linesep
# 换行符
# linux:`\n`

||Linux|Win|Unix| |———|——————|———|———| |sep|/|\|/(某些MAC:)| |pathsep|:|;|| |curdir|.||| |pardir|..||| |linesep|\n|\r\n||

  • 借助这些变量可以系统相关字符串操作的跨平台
  • win下目录组分隔符是\,大部分情况下看到\\是作为\ 转义字符,防止\和之后字符转义
    • 确认不会转义时,直接使用\也是可以的
    • 使用r''表示不转义也可以直接使用\

路径名工具

判断存在

1
2
3
4
5
os.path.isdir(r"C:\Users")
os.path.isfile(r"C:\Users")
# 判断路径名是简单文件、目录
os.path.exists(r"C:\Users")
# 判断路径名是否存在
  • os.stat配合stat模块有更丰富的功能

路径操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
pfile = os.path.join(r"C:\temp", "output.txt")
# 连接文件名、目录

os.path.split(pfile)
# 分离文件名、目录

os.path.dirname(pfile)
# 返回路径中目录
os.path.basename(pfile)
# 返回路径中
os.path.splitext(pfile)
# 返回文件扩展名

os.path.normpath(r"C:\temp/index.html")
# 调整路径为当前平台标准,尤其是分隔符混用时
os.path.abspath("index.html")
# 返回文件的**完整绝对路径名**
# 扩展`.`、`..`等语法
  • os.sep配合字符串.join.split方法可以实现基本相同 效果

目录、文件操作

1
2
3
4
5
6
7
8
9
10
11
12
os.mkdir(dirname)
os.rename(ori_name, dest_name)
os.remove(filename)
os.unlink(filename)
# unix下文件删除,同`os.remove`
os.chmod(filename, previlideges)
info = os.stat(filename)
# 命名元组表示的文件底层信息
# 可使用`stat`模块处理、解释信息
os.listdir(dirpath)
os.walk(rootdir, topdown=True/False)
# 遍历根目录下的整个目录树

os.listdir

  • 返回值:包含目录中所有条目名称的列表

    • 名称不带目录路径前缀
  • 需要注意的是:文件名同样有编码

    • 若参数为字节串,返回文件名列表也是字节串
    • 参数为字符串,返回文件名列表也是字符串
    • open函数也可以类似使用字节串确定需要打开的文件
    • glob.globos.walk内部都是通过调用os.listdir 实现,行为相同
  • glob模块也有遍历目录的能力

os.walk

  • 返回值:返回迭代器

    • 每个元素为(dirname, subdirs, subfile)
  • 参数

    • topdown:默认True,自顶向下返回

文件描述符、文件锁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
descriptor = os.open(path, flags, mode)
# 打开文件并返回底层描述符
os.read(descriptor, N)
# 最多读取N个字节,返回一个字节串
os.write(descriptor, string)
# 将字节串写入文件
os.lseek(descriptor, position, how)
# 移动文件游标位置
descriptor.flush()
# 强制刷出缓冲

new_fd = os.dup(fd)
# 创建文件描述符副本
os.dup2(fd_src, fd_dest)
# 将文件描述符`fd_src`复制至`fd_dest`
  • os通过调用文件的描述符来处理文件

  • 基于文件描述符的文件以字节流形式处理

    • 没有字符解码、编码、换行符转换
    • 除了缓冲等额外性能,基于描述符的文件和二进制文件模式 对象类似
  • 文件流对象、工具仅仅是在基于描述符的文件的封装

    • 可以通过.fileno()获得文件流对象对应文件描述符, sys.stdinsys.stdoutsys.stderr对应文件 描述符是:0、1、2

      1
      2
      os.write(1, b"hello world\n")
      sys.stdout.write("hello world\n")
    • 可以通过os.fdopen把文件描述符封装进文件流对象

      1
      2
      3
      fdfile = os.open("filename", (os.O_RDWR|os.O_BINARY))
      filstream = os.fdopen(fdfile, "r", encoding="utf-8",
      closefd=False)

os.open

1
2
3
4
5
def os.open(path,
flags,
mode=511, *,
dir_fd=None
)
  • 参数

    • mode:需要模式标识符进行二进制操作以得到需要的模式

      • os.O_RDWR
      • os.O_RDONLY
      • os.O_WRONLY
      • os.O_BINARY
      • os.O_EXCL:唯一访问权,是python在并发、进程 同步情况下锁定文件最便捷的方法
      • os.O_NONBLOCK:非阻塞访问
      • 其他模式选项参见os模块
  • 返回值:文件描述符

    • 整数代码、句柄,代表操作系统的中文件

退出进程

1
2
os._exit(0)
# 调用进程立即退出,不输出流缓冲、不运行清理处理器

异常信息

trackback模块

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])

参数处理

getopt模块

optparse模块

文件、目录

stat模块

  • 包含os.stat信息相关常量、函数以便跨平台使用
1
2
3
4
5
6
7
8
import stat

info = os.stat(filename)
info[stat.ST_MODE]
# `stat.ST_MODE`就是字符串
# 只是这样封装易于跨平台
stat.S_ISDIR(info.st_mode)
# 通过整数`info.st_mode`判断是否是目录
  • os.path中包含常用部分相同功能函数

glob模块

glob.glob

1
2
3
import glob

def glob.glob(pathname,*,recursive=False)
  • 参数

    • pathname:文件名模式
      • 接受shell常用文件名模式语法
        • ?:单个字符
        • *:任意字符
        • []:字符选集
      • .开头路径不被以上?*匹配
    • recursive
      • False:默认
      • True**将递归匹配所有子目录、文件
  • 返回值:匹配文件名列表

    • 目录前缀层次同参数
  • glob.glob是利用glob.fnmatch模块匹配名称模式

struct模块

struct模块:用于打包、解压二进制数据的调用

  • 类似于C语言中struct声明,需要指定二进制中数据类型
  • 可以使用任何一种字节序(大、小端)进行组合、分解
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import struct
data = struct.pack(">i4shf", 2, "spam", 3, 1.234)
# `>`:高位字节优先,大端
# `i`:整形数据
# `4s`:4字符字符串
# `h`:半整数
# `f`:浮点数
file = open("data.bin", "wb")
file.write(data)
# 二进制写入字节串
file.close()

file = open("data.bin", "rb")
bytes = file.read()
values = struct.unpack(">i4shf", data)
# 需要给出字节串存储格式

shutil模块

shutil模块:包含文件操作相关

todo

系统、信息

locale模块

1
2
3
4
import locale

locale.getpreferredencoding()
# 获取平台默认编码方案

dis模块

1
2
def dis.dis(func)
# 打印可拆解函数语句对应机器指令

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注册回调函数可能不会 被正常调用

signal模块

信号模块