Python05标准库
Python标准库
Python有一套很有用的标准库(standard library)。标准库会随着Python解释器,一起安装在电脑中的。它是Python的一个组成部分。
正则表达式 (re包)
正则表达式(regular expression)主要功能是从字符串(string)中通过特定的模式(pattern),搜索想要找到的内容。
语法
1 | 文档: https://docs.python.org/3/library/re.html |
1 | import re |
re.search()接收两个参数,第一个'[0-9]'就是正则表达式, 意为从字符串想要找的是从0到9的一个数字字符
re.search()如果从第二个参数找到符合要求的子字符串,就返回一个对象m,你可以通过m.group()的方法查看搜索到的结果。如果没有找到符合要求的字符,re.search()会返回None
正则表达式的函数
1 | # 搜索整个字符串,直到发现符合的子字符串。 |
写一个正则表达式
正则表达式的常用语法:
单个字符:
1 | . 任意的一个字符 |
重复
1 | * 重复 >=0 次 |
位置
1 | ^ 字符串的起始位置 |
返回控制
我们有可能对搜索的结果进行进一步精简信息。
1 | m = re.search("output_(\d{4})","output_1988.txt") |
我们还可以使用 ?P<name> 将群命名,以便更好地使用m.group查询:
1 | m = re.search("output_(?P<year>\d{4})", "output_1988.txt") |
时间与日期 (time, datetime包)
time包
time包基于C语言的库函数(library functions)。
1 | import time |
datetime包
简介
datetime包是基于time包的一个高级包, 为我们提供了多一层的便利。
datetime可以理解为date和time两个组成部分。date是指年月日构成的日期(相当于日历),time是指时分秒微秒构成的一天24小时中的具体时间(相当于手表)。你可以将这两个分开管理(datetime.date类,datetime.time类),也可以将两者合在一起(datetime.datetime类)。
1 | import datetime |
运算
datetime包还定义了时间间隔对象(timedelta)。一个时间点(datetime)加上一个时间间隔(timedelta)可以得到一个新的时间点(datetime)。比如今天的上午3点加上5个小时得到今天的上午8点。同理,两个时间点相减会得到一个时间间隔。
1 | import datetime |
datetime对象与字符串转换
用格式化读取的方式读取时间信息。
1 | from datetime import datetime |
1 | # 案例 |
数学与随机数 (math包,random包)
math包补充了很多的函数。如果想要更加高级的数学功能,可以考虑选择标准库之外的numpy和scipy项目,它们不但支持数组和矩阵运算,还有丰富的数学和物理方程可供使用。
random包可以用来生成随机数。随机数不仅可以用于数学用途,还经常被嵌入到算法中,用以提高算法效率,并提高程序的安全性。
math包
math包主要处理数学相关的运算。math包定义了两个常数:
1 | math.e # 自然常数e |
此外,math包还有各种运算函数 (下面函数的功能可以参考数学手册):
1 | math.ceil(x) # 对x向上取整,比如x=1.2,返回2 |
三角函数: math.sin(x), math.cos(x), math.tan(x), math.asin(x), math.acos(x), math.atan(x)
这些函数都接收一个弧度(radian)为单位的x作为参数。
角度和弧度互换: math.degrees(x), math.radians(x)
双曲函数: math.sinh(x), math.cosh(x), math.tanh(x), math.asinh(x), math.acosh(x), math.atanh(x)
特殊函数: math.erf(x), math.gamma(x)
random包
1 | 文档 : https://docs.python.org/3/library/random.html |
1 | import random |
路径与文件 (os.path包, glob包)
os.path包
os.path包主要是处理路径字符串,比如说’/home/vamei/doc/file.txt’,提取出有用信息。
1 | import os.path |
os.path还可以查询文件的相关信息(metadata)。文件的相关信息不存储在文件内部,而是由操作系统维护的,关于文件的一些信息(比如文件类型,大小,修改时间)。
1 | import os.path |
glob包
glob.glob()。接受一个Linux式的文件名格式表达式(filename pattern expression),列出所有符合该表达式的文件(与正则表达式类似),将所有文件名放在一个表中返回。所以glob.glob()是一个查询目录下文件的好方法。
文件名格式表达式:
| Filename Pattern Expression | Python Regular Expression |
|---|---|
| * | .* |
| ? | . |
| [0-9] | same |
| [a-e] | same |
| [^mnp] | same |
1 | # 示例 |

文件管理 (部分os包,shutil包)
Python标准库则允许我们从Python内部管理文件。
os包
os包包括各种各样的函数,以实现操作系统的许多功能。这个包非常庞杂。os包的一些命令就是用于文件管理。
常用的有:
1 | import os |
shutil包
- copy(src, dst) 复制文件,从src到dst。相当于$cp命令。
- move(src, dst) 移动文件,从src到dst。相当于$mv命令。
1 | import shutil |
存储对象 (pickle包,cPickle包)
Python“一切皆对象”, 在Python中,无论是变量还是函数,都是一个对象。如何将对象保存到文件,并储存在硬盘上呢
计算机的内存中存储的是二进制的序列 (当然,在Linux眼中,是文本流)。我们可以直接将某个对象所对应位置的数据抓取下来,转换成文本流 (这个过程叫做serialize),然后将文本流存入到文件中。由于Python在创建对象时,要参考对象的类定义,所以当我们从文本中读取对象时,必须在手边要有该对象的类定义,才能懂得如何去重建这一对象。
从文件读取时,对于Python的内建(built-in)对象 (比如说整数、词典、表等等),由于其类定义已经载入内存,所以不需要我们再在程序中定义类。但对于用户自行定义的对象,就必须要先定义类,然后才能从文件中载入对象
pickle包
将内存中的对象转换成为文本流:
1 | import pickle |

重建对象
首先从文本中读出文本,存储到字符串 (文本文件的输入输出)。然后使用pickle.loads(str)的方法,将字符串转换成为对象。要记得,此时我们的程序中必须已经有了该对象的类定义。
1 | import pickle |

cPickle包
cPickle包的功能和用法与pickle包几乎完全相同 (其存在差别的地方实际上很少用到),不同在于cPickle是基于c语言编写的,速度是比pickle包的快。如果想使用cPickle包,我们都可以将import语句改为:
1 | import cPickle as pickle |
子进程 (subprocess包)
subprocess包主要功能是执行外部的命令和程序。比如在Python中调用wget程序。从这个意义上来说,subprocess的功能与shell类似。
常用的封装函数
使用subprocess包中的函数创建子进程的时候,要注意:
- 1 在创建子进程之后,父进程是否暂停,并等待子进程运行。
- 2 函数返回什么
- 3 当returncode不为0时,父进程如何处理。
1 | import subprocess |
Popen()
上面的三个函数都是基于Popen()的封装(wrapper)
与上面的封装不同,Popen对象创建后,主程序不会自动等待子进程完成。
我们必须调用对象的wait()方法,父进程才会等待 (也就是阻塞block)
1 | import subprocess |
子进程的文本流控制
我们可以在Popen()建立子进程的时候改变标准输入、标准输出和标准错误,
并可以利用subprocess.PIPE将多个子进程的输入和输出连接在一起,构成管道(pipe):
1 | import subprocess |
还可以利用communicate()方法来使用PIPE给子进程输入:
1 | import subprocess |
信号 (signal包)
1 | # signal包负责在Python程序内部处理信号,典型的操作包括预设信号处理函数,暂停并等待信号,以及定时发出SIGALRM等。 |
定义信号名
signal包定义了各个信号名及其对应的整数,比如:
1 | import signal |
Python所用的信号名和Linux一致。你可以通过以下命令查询:
1 | man 7 signal # 这一条命令在实验楼环境中无法执行成功,需要在真实的 Linux 系统中才能成功执行 |
预设信号处理函数
signal包的核心是使用**signal.signal()**函数来预设(register)信号处理函数,如下所示:
1 | signal.signal(signalnum, handler) # signalnum为某个信号,handler为该信号的处理函数 |
1 | # 示例 |

myHandler的两个参数一个用来识别信号(signum),另一个用来获得信号发生时,进程栈的状况(stack frame)。这两个参数都是由signal.singnal()函数来传递的。
当程序运行到signal.pause()的时候,进程暂停并等待信号。此时,通过按下CTRL+Z向该进程发送SIGTSTP信号。可以看到,进程执行了myHandle()函数, 随后返回主程序,继续执行。
定时发出SIGALRM信号
函数signal.alarm(),它被用于在一定时间之后,向进程自身发送SIGALRM信号:
1 | import signal |
效果如下:
1 | not yet |
发送信号
signal包的核心是设置信号处理函数。除了signal.alarm()向自身发送信号之外,并没有其他发送信号的功能。
但在os包中,有类似于linux的kill命令的函数,分别为:
1 | import os |
多线程与同步 (threading包)
Python主要通过标准库中的threading包来实现多线程。
多线程售票以及同步
使用mutex (也就是Python中的Lock类对象) 来实现线程的同步:
1 | import threading,time,os |
使用两个全局变量,一个是i,用以储存剩余票数;一个是lock对象,用于同步线程对i的修改。
此外,在最后的for循环中,我们总共设置了10个线程。每个线程都执行booth()函数。线程在调用start()方法的时候正式启动 (实际上,计算机中最多会有11个线程,因为主程序本身也会占用一个线程)。
Python使用threading.Thread对象来代表线程,用threading.Lock对象来代表一个互斥锁 (mutex)。
有两点需要注意:
- 在函数中使用global来声明变量为全局变量,从而让多线程共享i和lock。如果不这么声明,由于i和lock是不可变数据对象,它们将被当作一个局部变量。如果是可变数据对象的话,则不需要global声明。我们甚至可以将可变数据对象作为参数来传递给线程函数。这些线程将共享这些可变数据对象。
- 在booth中使用了两个doChore()函数。可以在未来改进程序,以便让线程除了进行i=i-1之外,做更多的操作,比如打印剩余票数,找钱,或者喝口水之类的。第一个doChore()依然在Lock内部,所以可以安全地使用共享资源 (critical operations, 比如打印剩余票数)。第二个doChore()时,Lock已经被释放,所以不能再去使用共享资源。这时候可以做一些不使用共享资源的操作 (non-critical operation, 比如找钱、喝水)。
OOP创建线程
通过面向对象 (OOP, object-oriented programming) 的方法实现多线程,其核心是继承threading.Thread类。
上面的for循环中已经利用了threading.Thread()的方法来创建一个Thread对象,并将函数booth()以及其参数传递给改对象,并调用start()方法来运行线程。OOP的话,通过修改Thread类的**run()**方法来定义线程所要执行的命令。
1 | import threading,time,os |
定义了一个类BoothThread, 这个类继承自thread.Threading类。
然后把上面的booth()所进行的操作统统放入到BoothThread类的run()方法中。
没有使用全局变量声明global,而是使用了一个词典monitor存放全局变量,然后把词典作为参数传递给线程函数。
由于词典是可变数据对象,所以当它被传递给函数的时候,函数所使用的依然是同一个对象,相当于被多个线程所共享。
其他
threading.Thread对象: 我们已经介绍了该对象的start()和run(), 此外:
- **join()**方法,调用该方法的线程将等待直到该Thread对象完成,再恢复运行。这与进程间调用wait()函数相类似。
下面的对象用于处理多线程同步。对象一旦被建立,可以被多个线程共享,并根据情况阻塞某些进程。
下面的对象用于处理多线程同步。对象一旦被建立,可以被多个线程共享,并根据情况阻塞某些进程。
threading.Lock对象: mutex, 有acquire()和release()方法。
threading.Condition对象: condition variable,建立该对象时,会包含一个Lock对象 (因为condition variable总是和mutex一起使用)。可以对Condition对象调用acquire()和release()方法,以控制潜在的Lock对象。此外:
- **wait()**方法,相当于cond_wait()
- **notify_all()**,相当与cond_broadcast()
- **nofify()**,与notify_all()功能类似,但只唤醒一个等待的线程,而不是全部
threading.Semaphore对象: semaphore,也就是计数锁(semaphore传统意义上是一种进程间同步工具)。创建对象的时候,可以传递一个整数作为计数上限 (sema = threading.Semaphore(5))。它与Lock类似,也有Lock的两个方法。
threading.Event对象: 与threading.Condition相类似,相当于没有潜在的Lock保护的condition variable。对象有True和False两个状态。可以多个线程使用wait()等待,直到某个线程调用该对象的**set()方法,将对象设置为True。线程可以调用对象的clear()方法来重置对象为False状态。