除了前面提及的的数据分析第三方库,Python 中也自带了许多有用的标准库,提供了一系列基础接口。此外本文还将介绍一些项目中常用的第三方库,更多细节可以在交互式窗口 中输入 dir(xx)
和 help(xx)
检索。
标准库 标准库通常与 Python 发行版捆绑安装,其提供的组件涉及范围十分广泛,此处仅介绍常用库。
导入标准库时建议使用 import os
风格而非 from os import *
,这样在调用的时候就可以用 os.open()
,避免覆盖内置的函数 open()
。
os 操作系统接口 Python 中与操作系统交互的标准库,可以完成目录管理、进程管理、调度器接口等操作。
在深度学习中常用于创建目录、遍历目录、操作路径 。使用方法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import os os.mkdir(path) os.makedirs(path) if not os.path.exists(path): os.makedirs(path) os.makedirs(path, exist_ok=True ) os.path.join('path' ,'abc' ,'1.txt' ) os.path.join('./path' ,'abc' ,'1.txt' )
os 模块还能设置环境参数,在深度学习中常用于设置 GPU 相关参数,但是必须放在 main 函数的入口处,例如:
1 2 3 4 5 if __name__ == '__main__' : os.environ['CUDA_LAUNCH_BLOCKING' ] = '1' os.environ["TOKENIZERS_PARALLELISM" ] = "false" os.environ["CUDA_VISIBLE_DEVICES" ] = '0'
此外,os 模块还能在脚本中直接使用终端命令,只需要 os.command(" ")
就能执行,常用于批量使用复杂命令。
time 时间访问和转换 Python 中处理时间的标准库,提供系统级精确计时功能(可以用于程序性能分析)。其主要功能分为三块:时间获取、时间格式化、程序计时,这里介绍计时相关内容。
sleep(s)
: s
为休眠时间,单位秒,可以是浮点数。perf_counter()
:返回一个 CPU 级别的精确时间计数值,单位为秒。后者的使用由于计数器起点 不确定,连续调用求差值 才有意义:
1 2 3 4 5 6 7 8 import timeimport numpy as np arr = np.arange(100000000 ) start = time.perf_counter() arr = arr * 2 end = time.perf_counter() print ('numpy time cost: %.3f s' % (end - start))
当然,如果只是想测试单条指令的用时,可以在 IPython 中调用如下命令,会多次执行计算平均值和方差 :
1 2 3 >>> %%timeit>>> np.convolve(arr1, arr2)
datetime 日期与时间 Python 中用于处理日期和时间的标准库,包括三个主要的类:date
、time
和 datetime
。这些类用于表示日期、时间和日期时间 。此外,还有一些函数和常量可用于日期和时间的处理。
date
类用于表示日期,有三个属性:year
、month
和 day
。time
类用于表示时间,有三个属性:hour
、minute
和 second
。datetime
类用于表示日期时间,是 date
类和 time
类的组合。下面是具体命令,使用strftime()
方法将date
对象格式化为字符串:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import datetime d = datetime.date(2023 , 4 , 4 )print (d) print (d.strftime('%Y/%m/%d' )) t = datetime.time(12 , 30 , 45 )print (t) print (t.strftime('%H:%M:%S' )) dt = datetime.datetime(2023 , 4 , 4 , 12 , 30 , 45 )print (dt) print (dt.strftime('%Y-%m-%d %H:%M:%S' ))
以下是一些常用的命令,可用于日期和时间的处理:
datetime.date.today()
:返回当前日期的date
对象。1 2 today = datetime.date.today()print (today)
datetime.datetime.now()
:返回当前日期时间的datetime
对象。1 2 now = datetime.datetime.now()print (now)
datetime.datetime.strptime()
:将字符串解析为datetime
对象。需要指定字符串格式。1 2 3 dt_str = '2023-04-04 12:30:45' dt = datetime.datetime.strptime(dt_str, '%Y-%m-%d %H:%M:%S' )print (dt)
datetime.timedelta()
:用于计算时间差。1 2 3 delta = datetime.datetime.timedelta(days=3 ) new_date = today + deltaprint (new_date)
datetime.datetime.replace()
:用于更改datetime
对象的某些属性。1 2 new_dt = dt.replace(hour=13 , minute=0 , second=0 )print (new_dt)
datetime.datetime.fromtimestamp()
:用于从 Unix TimeStamp 得到 datetime。1 2 dt = datetime.datetime.fromtimestamp(ts) print (dt)
logging 日志记录 Python 中自带的日志模块,基本使用方法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import logging logger = logging.getLogger(__name__) logging.basicConfig(level=logging.INFO, format ='[%(asctime)s] [%(levelname)s] %(message)s' , datefmt='%a, %d %b %Y %H:%M:%S' , filename=f'{Model_name} .log' ) logging.debug('This is a debug message' ) logging.info('This is a info message' ) logging.warn('This is a warn message' ) logging.error('This is a error message' ) logging.critical('This is a critical message' )
所谓日志级别,就是日志中只关注大于设定级别 的内容:
DEBUG 详细信息,调试时对所有问题感兴趣 INFO 事情按预期工作,训练模型最常用 WARNING 可能的意外,但仍能工作 ERROR 错误,软件已不能执行一些功能 CRITICAL 严重错误,软件已不能继续运行
这里也可以换用暴力点的输出日志方法,直接用 Linux 的管道加 tee -a
命令,将所有输出流复制到文件中,但是这种方法不好处理 tqdm 产生的 bar。
glob 文件名模式匹配 Python 中用于查询符合特定规则的文件路径名的标准库,不用遍历整个目录判断每个文件是不是符合。使用 glob.glob('路径')
调用,返回一个符合条件的路径字符串的 List 结构,支持通配符 *
、**
、?
、[]
,具体用法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import globfor name in glob.glob('dir/*' ): pass for name in glob.glob('dir/*/*' ): pass for name in glob.glob('dir/**' , recursive=True ): pass for name in glob.glob('dir/file?.txt' ): pass for name in glob.glob('dir/*[0-9].txt' ): pass
注意:*
通配符默认不匹配以 .
开始的文件,如 .config
,如果有这类文件,则需要显式标注点号。
csv 文件读写 Python 中用来读取 CSV 文件的标准库,一个 CSV 文件经常包含若干行,每行包含若干列,列和列之前通常使用逗号分隔 。其内部实现了一个 reader 类和 writer 类,用于读写序列化的数据:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import csvwith open ('some.csv' , 'r' , newline='' ) as f: reader = csv.reader(f) for row in reader: print (row) data = [['Name' , 'Age' , 'Country' ], ['John' , '25' , 'USA' ], ['Alice' , '30' , 'Canada' ], ['Bob' , '35' , 'UK' ]]with open ('some.csv' , 'a' , newline='' ) as f: writer = csv.writer(f) writer.writerows(data)with open ('some.csv' , 'a' , newline='' ) as f: writer = csv.writer(f) writer.writerow(['Name' , 'Age' , 'Country' ]) writer.writerow(['John' , '25' , 'USA' ])
注意:如果没有指定 newline=''
,则嵌入引号中的换行符将无法正确解析,并且在写入时,使用 \r\n
换行的平台会有多余的 \r
写入。由于 csv 模块会执行自己的换行符处理,因此指定 newline=''
应该总是安全的 。
在定义 reader 或 writer 时,可以指定以下参数:
delimiter=','
每一列的分隔符,默认为逗号,但在文本字符串中更常用 \t
以避免错误分隔quotechar='"'
为引号字符,默认为单引号,用于判断何时为字符串escaoechar='\\'
为转义字符,默认为反斜杠,不需要修改除此之外,本库还针对每一行为字典的 JSONL 文件 定义了读写方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 with open ('some.csv' , 'r' , newline='' ) as f: reader = csv.DictReader(f) for row in reader: print (row['Name' ], row['Age' ], row['Country' ]) data = [{'Name' :'John' , 'Age' :'25' , 'Country' :'USA' }, {'Name' :'Alice' , 'Age' :'30' , 'Country' :'Canada' }, {'Name' :'Bob' , 'Age' :'35' , 'Country' :'UK' }]with open ('some.csv' , 'r' , newline='' ) as f: fieldnames = ['Name' , 'Age' , 'Country' ] writer = csv.DictWriter(f, fieldnames=fieldnames) writer.writeheader() writer.writerows(data)
csv 库的功能大部分被 pandas 所包含,如果结构化处理数据的需求,推荐直接用 pandas 的 csv 读写函数。
argparse 命令行参数解析 Python 中用于解析命令行参数和选项的标准库。在复现较大型项目时,通常由工具脚本调用命令行参数,这些参数以链表形式存储于 sys 模块的 argv 变量,直接处理较为麻烦,因此有了该标准库。使用范例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import argparsedef read_args (): parser = argparse.ArgumentParser(description="You should add those parameter!" ) parser.add_argument("--path" , type =str , required=True , help ="The path of file" ) parser.add_argument("--input_num" , "-n" , type =int , default=50 ) parser.add_argument("--mode" , type =str , choices=['train' , 'test' ]) parser.add_argument('--use_clip' , action='store_true' ) args = parser.parse_args() return argsif __name__ == '__main__' : my_args = read_args() print (my_args.path)
re 正则表达式 Python 中正则表达式匹配的标准库。模式和被搜索的字符串既可以是 Unicode 字符串(str) ,也可以是 8 位字节串(byte),但必须一致。
关于正则表达式的语法此处不过多介绍,仅给出一些示例:
1 2 3 4 5 >>> import re>>> re.findall(r'\bf[a-z]*' , 'which foot or hand fell fastest' ) ['foot' , 'fell' , 'fastest' ]>>> re.sub(r'(\b[a-z]+) \1' , r'\1' , 'cat in the the hat' )'cat in the hat'
注意:若只需要简单的功能,应该首先考虑字符串方法,如 .replace()
、.split()
等,更易于阅读和调试。
zipfile 文件压缩 Python 标准库中的一个模块,用于创建、读取和操作 ZIP 文件。主要作用包括:创建 ZIP 文件、解压 ZIP 文件、读取 ZIP 文件(但不进行解压)、操作 ZIP 文件(增删改)。
其中,所有的方法被封装到 zipfile.ZipFile
类,使用方法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import zipfilewith zipfile.ZipFile('archive.zip' , 'w' ) as zipf: zipf.write('file1.txt' ) zipf.write('file2.txt' )with zipfile.ZipFile('existing.zip' , 'a' ) as zipf: zipf.write('new_file.txt' ) with zipfile.ZipFile('archive.zip' , 'r' ) as zipf: zipf.extractall('extracted_dir' )
对于一个已有的 ZIP 文件,可以进行的操作如下:
1 2 3 4 5 6 7 8 9 10 11 import zipfilewith zipfile.ZipFile('archive.zip' , 'r' ) as zipf: file_list = zipf.namelist() print (zipf.getinfo('file1.txt' ))with zipfile.ZipFile('archive.zip' , 'w' ) as zipf: with zipf.open ('file1.txt' , 'r' ) as file: zipf.writestr('file1.txt' , file.read())
zipfile 库在实际应用中被广泛使用,特别是在需要处理大量文件或目录 的情况下,使用 ZIP 文件可以方便地进行文件的打包、传输、存储和管理。
traceback 异常处理和调试 Python 中用于异常处理和调试的标准库之一。它允许查看程序执行时发生的异常信息 ,并追踪函数调用的堆栈 。常用的函数有以下两种:
traceback.print_tb(tb, limit=None, file=None)
:打印当前异常的堆栈跟踪信息。它接受以下参数:tb
:表示异常信息的 traceback 对象。limit
:可选参数,用于指定打印堆栈跟踪信息的层数。如果不指定,将打印整个堆栈。file
:可选参数,用于指定输出的文件对象。如果不指定,将打印到标准输出。traceback.print_exception(etype, value, tb, limit=None, file=None, chain=True)
:打印异常信息,包括异常类型、异常值和堆栈跟踪信息。它接受以下参数:etype
:异常类型。value
:异常值。tb
:表示异常信息的 traceback 对象。limit
:可选参数,用于指定打印堆栈跟踪信息的层数。如果不指定,将打印整个堆栈。file
:可选参数,用于指定输出的文件对象。如果不指定,将打印到标准输出。chain
:可选参数,如果为 True
,将打印异常链中的所有异常信息。默认为 True
。1 2 3 4 5 6 7 import tracebacktry : result = 10 / 0 except Exception as e: traceback.print_tb(e.__traceback__) traceback.print_exception(type (e), e, e.__traceback__)
第三方库 第三方库通常需要 pip install
,建议安装在虚拟环境中,按需取用。
pipreqs 依赖安装 当我们写好一个 Python 项目之后,如果要开源代码,为了让别人能快速运行项目,一般可以提供一个 requirements.txt
的,用以配置环境。
pipreqs
是用于自动生成上述文件的一个第三方库,使用 pip install pipreqs
安装,进入项目根目录 ,执行 pipreqs ./
即可。如果出现编码错误,可以指定编码方式解决:
1 pipreqs ./ --encoding utf-8
其他人则可以使用以下命令配置环境:
1 pip install -r requirements.txt
codecs 编码转换 codec
意为字符的编解码器(Encoder&Decoder),Python 对多国语言的处理是支持的很好的,它可以处理现在任意编码的字符。在作字符转换时,Python 会借助于内部的编码(Unicode),转换的过程是这样的:原有编码 -> 内部编码 -> 目的编码
。而这个转换的过程就需要用到编解码器。
一种基本的用法是通过 lookup
获取特定的编码器,再用其与 Unicode 进行转换:
1 2 3 4 5 6 7 8 9 >>> import codecs>>> codec1 = codecs.lookup("utf-8" )>>> a = "你好" >>> b = codec1.encode(a)>>> print (b) (b'\xe4\xbd\xa0\xe5\xa5\xbd' , 2 ) >>> c = codec1.decode(b[0 ])>>> print (c) ('你好' , 6 )
另一种更常用的用法是对文件 字符编码的处理,常用于数据处理任务:
1 2 3 import codecs file = codecs.open (path, encoding='latin-1' ).readlines()
json 文件读写 JSON(JavaScript Object Notation)是一种轻量级的数据交换格式 ,采用完全独立于语言的文本格式,但格式类似 C 系语言。易于人阅读和编写,同时也易于机器解析和生成(一般用于提升网络传输速率)。使用 pip install json-parser
安装。
Python 中的 List、Dict 最常用于 JSON 文件的转换,在此前要先将数据类型转为字符串 Str。json 模块提供了四个功能:
dumps
:把数据类型转换成字符串dump
:把数据类型转换成字符串并存储在文件中loads
:把字符串转换成数据类型load
:把文件打开从字符串转换成数据类型基本使用方法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import json test_dict = {'bigberg' : [7600 , {'details' : [['iPhone' , 6300 ], ['Bike' , 800 ], ['shirt' , 300 ]] }] } json_str = json.dumps(test_dict) new_dict = json.loads(json_str) with open ("../config/record.json" ,"w" ) as f: json.dump(new_dict,f) with open ("../config/record.json" ,'r' ) as load_f: load_dict = json.load(load_f)
注意:JSON 文件通常是不可读的 ,因为其将整个列表或字典压缩到同一行字符串中,以减少文件体积。如果要增加其可读性,可以在转换时指定缩进 ,例如 dump(data, file, indent=4)
。
注意:JSON 保存输出时默认会将非 ASCII 字符进行 Unicode 转移 ,生成 \u...
,如果想要禁止这一过程,可以在 dump 中使用 ensure_ascii=False
。该设置不会影响 load 方法。
此外,还有一种 JSONL 文件,一行为一条记录或数据(可读性稍好),常用作日志文件 或数据集 ,支持用 json 操作:
1 2 3 4 5 6 7 8 9 10 data = []with open ("input.jsonl" ,'r' ) as f: data_in = f.readlines() for obj in data_in: data.append(json.loads(obj)) with open ("output.jsonl" ,"w" ) as f: for obj in data: f.write(json.dumps(obj) + '\n' )
常见错误:在使用 dump 的时候报错 TypeError: Object of type int64 is not JSON serializable,这是由于 dump 不能处理 NumPy 中的数据类型 ,此时需要手动给整数套上 int()
、浮点数套上 float()
、多维数组加上 .tolist()
。也可以通过重加载 JSONEncoder 实现上述功能:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import jsonimport numpy as npclass NpEncoder (json.JSONEncoder ): def default (self, obj ): if isinstance (obj, np.integer): return int (obj) if isinstance (obj, np.floating): return float (obj) if isinstance (obj, np.ndarray): return obj.tolist() return super (NpEncoder, self).default(obj) json.dumps(data, cls=NpEncoder)
jsonlines 文件读写 当然,上述操作也可以使用专门的 JSONL 处理模块替代:
1 2 3 4 5 6 7 8 9 10 import jsonlines data = []with jsonlines.open ('input.jsonl' , encoding='utf-8' ) as reader: for obj in reader: data.append(obj) with jsonlines.open ('output.jsonl' , mode='w' , encoding='utf-8' ) as writer: for obj in data: writer.write(obj)
tqdm 进度条 tqdm 是一个用于在 Python 命令行中添加进度条 的库。它提供了一种简单而有效的方式来监测长时间运行的任务的进度,只需用 tqdm(iterable)
装饰任何可迭代对象 ,就能直观地看到进度条:
1 2 3 4 5 import timefrom tqdm import tqdm for i in tqdm(range (1000 )): time.sleep(0.1 )
此时命令行输出的样式如下:
1 35 %|██████████ | 35 /100 [00 :02 <00 :05 , 18.78 it/s]
在 tqdm()
中传入参数可以修改样式:
bar_format="{l_bar}{bar}| {n_fmt}/{total_fmt}"
设置进度条样式ncols=80
设置进度条总长度,默认为填充整行命令行desc="Processing"
设置前缀,默认为空,即只显示百分比postfix={"loss": 0, "acc": 0}
设置额外的后缀项目leave=True
进度条在完成后是否保持可见,默认为 True实际上 tqdm()
除了可以直接传入可迭代对象,也可以使用 with
语句来创建了一个 tqdm 的上下文管理器 。通过设置 total
参数为迭代对象的长度来告诉 tqdm 进度条的总长度,并使用 update()
来在每轮迭代中更新。
1 2 3 4 5 6 with tqdm(total=len (train_loader)) as pbar: for item, label in train_loader: pbar.set_description(f"Processing..." ) pbar.set_postfix(loss=loss, acc=acc) pbar.update()
那么如何优雅地在深度学习训练框架 中应用 tqdm 呢?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 for epoch in range (num_epochs): losses = [] num_correct = 0 with tqdm(total=len (train_loader), ncols=20 ) as pbar: for idx, (inputs, targets) in enumerate (train_loader): optimizer.zero_grad() inputs, targets = inputs.to(device), targets.to(device) preds = model(inputs) loss = criterion(preds, targets) losses.append(loss) loss.backward() optimizer.step() num_correct += (predictions == targets).sum () acc = num_correct / (idx + 1 ) * batch_size pbar.set_description(f'Epoch [{epoch} /{num_epochs} ]' ) pbar.set_postfix(loss=round (loss.item(),4 ), acc=round (acc,4 )) pbar.update()
pprint 美化输出 全称 Pretty Print,用于替代原来的 print()
函数,主要用于打印复杂的数据结构对象,例如多层嵌套的列表、元组和字典等。常用于多层嵌套的内容 (例如复杂的 json 文件),或者有超多的元素 (例如在列表中存了很多 URL 链接)。功能有:
设置合适的行宽度,作适当的换行; 设置打印的缩进、层级,进行格式化打印; 判断对象中是否有无限循环,并优化打印内容。 基本使用方法如下:
1 2 3 from pprint import pprint pprint(json.loads(sample_file[0 ]))
PIL 图像处理 全称 Python Imaging Library,有时也称 pillow,使用 pip install pillow
安装。支持众多图像格式,可用于执行裁剪、大小调整、旋转、滤镜效果等操作,引入时通常使用 from PIL import Image
。
基本使用方法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 from PIL import Image image = Image.open ('input.jpg' ) image.show() image.save('output.jpg' )print (image.mode, image.size, image.format ) grey_image = image.convert('L' ) grey_image.show() r, g, b = image.split() im = Image.merge('RGB' , (b, g, r))
支持对图像整体、局部进行像素级操作:
1 2 3 4 5 6 7 8 9 10 11 12 box = (100 , 100 , 300 , 300 ) region = image.crop(box) region = region.transpose(Image.ROTATE_180) image.paste(region, box) im = image.resize((300 , 300 )) im = image.rotate(45 ) im = image.transpose(Image.FLIP_LEFT_RIGHT) im = image.transpose(Image.FLIP_TOP_BOTTOM) im = image.point(lambda i: i * 1.2 ) im = image.point(lambda i: i > 128 and 255 )
还支持与 Numpy 的交互,用于手动实现图像处理算法:
1 2 3 array = np.array(image)print (array.shape) i = Image.fromarray(array)
注意,PIL 只能完成基础的图像处理,更高端的操作还得用到 OpenCV 的 cv2 模块,例如:仿射变换、基本绘制、随机变化等。
Jupyter Notebook Jupyter Notebook 有两种键盘输入模式:
编辑模式 ,允许你往单元中键入代码或文本;这时的单元框线是绿色的。命令模式 ,键盘输入运行程序命令;这时的单元框线是灰色。命令模式(按键 Esc 开启)快捷键 :
Enter :转入编辑模式Ctrl-Enter :运行本单元Shift-Enter :运行本单元,选中下个单元Alt-Enter :运行本单元,在其下插入新单元Y :单元转入代码状态M :单元转入 Markdown 状态方向键 :选中上方、下方单元A :在上方插入新单元B :在下方插入新单元X :剪切选中的单元C :复制选中的单元Shift-V :粘贴到上方单元V :粘贴到下方单元Z :恢复删除的最后一个单元Shift-M :合并选中的单元编辑模式(Enter 键启动)下快捷键 :
Tab :代码补全或缩进Shift-Tab :提示Esc :进入命令模式Ctrl-Enter :运行本单元Shift-Enter :运行本单元,选中下一单元Alt-Enter :运行本单元,在下面插入一单元