常用的模块 (modules)

写在前面

Awesome xxx 的项目,通常会很有『价值』,相当于代码世界里的 Hao123
Python 也有一个 Awesome Python 的项目,更多也可以参看 https://github.com/vinta/awesome-python
Awesome 通常为了不遗漏,很容易变得臃肿,Star (打分) 容易高,本身却又没有什么技术含量。

Awesome Python 对你来说,有无价值,我无法评判,当一个 List 过于丰富的时候,它的参考价值未必高,『全有』跟『没有』的区别不算太大。

另外,个人的倾向,如果引入一个 package/module,要先判断有无必要,如果可以不引入,那就不引入。如果引入的 package,粗略看看源码,过于繁杂,而引入之后的功用又不需要如此的代码,或许就可以考虑自己再写一个简单的。
而且,自己写的,慢慢积累下来,即使是简单的 module,到最后,都会成为一笔宝贵的财富。

本节的内容,主要说说我自己使用的一些 modules,或许有些参考价值。至于某个 module 的具体用法,就不做介绍,自己搜索引擎找一下,或者阅读 module 自身的文档,或者看下对应的源码,就可以了。

Python 内置 (标准库)

os

osoperating system (操作系统) 的缩写。
os.path 这个子模块,应该是蛮常用的,可以对路径进行操作、判断是文件还是文件夹。
os.popenos.makedirsos.listdir 等都是常见的函数,有些也经常会连用,比如 os.path.abspath(os.curdir) 可以获得当前工作目录的完整路径。

sys

syssystem (系统) 的缩写,倾向于 Python 系统,而不是操作系统。
sys.path 之前也介绍过了,sys 上还有些 hook 提供, sys.exc_info() 还能获得当前的异常 (错误) 信息。
如果通过命令行直接运行 .py 脚本文件,我们一般也会从 sys.argv 获取命令行窗口传过来的参数。具体你可以写个代码试试,看看 sys.args 的第一个参数会是什么。

re

正则表达式的 module,必备模块之一。
普通字符串的操作,过于简单了,当你希望对文本有更复杂些的分析、操作,那么正则是逃不掉的。
正则 的基本语法,除了 Python,还有 Node.js(Javascript)、Ruby、PHP 等等中,也基本通用,虽然各自语言不同而略微有点差异。
关于如何学好正则,这是一个漫长的过长。不要强求,如果不是工作需要,也没有必要一次性就学会,用到了再去看文档,再反复试,慢慢就掌握了。
关于正则,要注意两点:

  1. 正确匹配: 也比较困难,有时候匹配不正确,但是结果凑巧对了,然后遇到其它情况,又必然会出错。
  2. 注意性能: 一般情况下,正则的性能足够高,如果自己写了比较复杂的正则语法,理解起来会晦涩,而且也一定要注意性能的问题,有时候重复性地匹配会导致性能急剧下降。

random

可以产生随机数,或者随机取值。

io

from io import StringIOStringIO 是蛮常用的。io 相当于是 input-output 的缩写。
StringIO 的作用,主要是将普通的字符串转为类似 file 的类型,这样就可以 read & write (读写) 了。
有 read & write,是 file 这类对象比较典型的特征,我们在一些场景下,会需要一个类似 file 的数据类型,提供一个 input (read) & output (write) 的调用。而在具体程序运行的过程中,未必都能有原生的 file 供使用,比如说对应的内容并没有存在磁盘上,这时需要这个 StringIO 来构建临时的 file。

json

载入 JSON 内容,转化为 Python 的数据格式;或者将 Python 的数据格式,转为 JSON 格式的字符串。
一般使用 json.loads & json.dumps,单词 loaddump 容易理解,为什么后面会有 s? 这里倒不是及物动词产生的 s,而是 string 的缩写,表示输入或输出的结果是字符串。如果没有这个 s,那就意味着操作的应该是一个 file 类似的数据对象。
这个 dumps & loads 的逻辑,在其它编码转化的过程中,也很常见,相当于约定俗成了。

time

对时间的操作,time.time() 会返回当前的时间戳 (浮点数),时间戳是相对于1970 年过了多少秒。
换句话说,只要当前时间是准确的,不管处于什么时区,在不同设备上运行 time.time() 得到的结果是一样的。

datetime

对日期的操作。
如果服务器端操作,存储某个时间,一般是会转成 UTC 的时间 (0 时区)。
datetime 的简单代码参考:

>>> import datetime
>>> n1 = datetime.datetime.now()
>>> n2 = datetime.datetime.utcnow()
>>> n1
datetime.datetime(2019, 6, 28, 19, 1, 54, 317435)
>>> n2
datetime.datetime(2019, 6, 28, 11, 2, 6, 245398)
>>> n1 - n2
datetime.timedelta(0, 28788, 72037)

urllib

处理 URL 用的,解码、压码,提取 URL 中的变量等等。

sqlite3

SQLite 是一个轻量的数据库,不需要服务器,只是一个数据文件,比如手机上的很多 App,其背后驱动的数据库通常是 SQLite。
sqlite3 就是 Python 中操作 SQLite 用的。
关于数据库,未来你可能会用到 Mongodb、MySQL、PostgreSQL 等数据库,在 Python 调用这些数据库接口 (也叫驱动、drive)时,会有个概念,叫 ORM。简单的 ORM 真的很简单,只需把一些常用的数据库查询、操作在 Python 中封装,这样最终调用的时候比较友好,如果未来有机会的话,你就自己写一个简单的 ORM 试试 (比如代码总量控制在 200 行以内)。


外部第三方

requests

处理 HTTP 请求,简单来说,就是抓取 Web 页面 (URL) 时候用的。
这是很常用的模块。比如很多网站的 API,本质上都是 HTTP 请求,你可以使用 requests 很方便地构建一个基于 Web API 的简单 module。

python-dateutil

处理 datetime 相关的逻辑不够用时,一般会用到这个模块,虽然叫 python-datetuil,实际使用过程中是 import dateutil

gevent

协程工具,也是常用而强大的模块。
其实也不用搞清楚 协程 是什么,把 gevent 简单当做异步并发的工具就可以了。并发的意思,我们举个例子: 比如 1 ~100 按顺序逐个打印,这个叫 同步(Synchronous) ,花了 100 个时间单位;并发的意思,就是 1 ~100 同时打印,自然先后顺序是不固定的,花了 1+ 个时间单位,这就叫 异步(Asynchronous)
为什么是 1+ 而不是 1 个时间单位呢? 因为异步的逻辑本身也是有开销的,如果这个开销忽略掉的话,那就是理想的 1 个时间单位了。

异步是很常见的场景,比如我们通过 《FirstWeb》应该已经理解,一个网页的背后,可能对应着一个函数。那么,当很多人都访问同一个网页的时候,肯定是不能等谁访问好了 (等待函数运行完毕) 再下一个人访问,而是要 并发 地对应。

pillow

这是 Python 上的图片处理工具,最开始是 PIL,但 PIL 没有继续更新,由 pillow 接过了衣钵。
但 Python 在处理图片上,仍然是有局限的,如果想追求更高的处理速度、更多的细节处理能力,就不能只局限于 Python 了。比如我会使用 GraphicsMagick,然后再 Python 去调用对应的接口。

另外,一个小的问题,图片一般有 PNG、JPEG、BMP、GIF 等格式,假设一张图片 10Mb,有没有办法只读取 1Kb 大小 (相当于原图的万分之一大小),就判断出它是什么图片类型吗?或者说,有没有办法判断出它是不是图片?
如果用 pillow 这个库,就可能会读取完整的文件,转为 Python 中的图片数据对象,然后再判断它是不是正确的图片类型;这样的话,效率太低了。所以,这个时候,pillow 这个库甚至都用不到。
你可以想想有什么办法去处理这个问题?当做一个小作业吧。

unidecode

把 unicode 字符串 (也就是 Python 3 中的 str 类型) 转为 ASCII码,不严谨的理解就是转为英文字母。
unicode 和 ASCII码肯定没这么简单可以做到互相转换,所以,就中文的实际场景而言,是转为拼音字符。比如我主要用它来自动构建 URL 的路径,如果路径上有中文,只是使用普通 URL 转码,非常难看。

>>> from unidecode import unidecode
>>> unidecode(u"北京")

jieba

这是中文分词工具。
分词是全文检索的基础,主要看使用场景,如果是对性能要求比较高的、正式的产品环境,肯定要采用其它技术方案。
比如我会使用 Elasticsearch 作为全文检索的引擎,在 Python 中则是调用相应的 API 。有些时候,技术方案的采用,应该是灵活应变的,因为自己会 Python,那么把所有的技术方案都聚焦于 Python 的领域,有可能会造成不必要的瓶颈。
但分词的唯一作用并不是用于检索,它可以有更多的想象。

shortuuid

UUID 的全称是 Universally Unique Identifier,简单来说,它能 (不断) 产生世界上唯一的一个 ID,但 Python 自带的 uuid 库生成的 UUID 长度有点长,有 32 位,shortuuid 的则比较短一些。

>>> import uuid
>>> uuid.uuid1()
UUID('54af7b63-99ab-11e9-adf9-acde48001122')
>>> uuid.uuid1().hex
'5a4173e699ab11e9a2e5acde48001122'
>>> import shortuuid
>>> shortuuid.uuid()
'VatFzGZUJBNpZqvgjGXevJ'

psutil

psutil 的主要功能是获取系统信息,比如 cpu 的利用率、磁盘的容量、磁盘已使用空间比率、内存的大小、进程的信息 等等。

HTML 分析

我们使用 requests 能获得页面的内容,但是要解析页面的内容,最原始的办法可能是使用 re (正则)了。
为什么需要解析页面的内容?不是所有的网站都会有 API 来提供数据,所以,我们需要从原始的 HTML 源码中萃取数据。
分析 HTML 页面,不少人会推荐 BeautifulSoup,但我个人而言,更喜欢 pyquery。

XML 分析

如果处理 XML 格式的数据解析,lxml 会是一个基础库,xmltodict 是另外一个库,它也依赖于 lxml。
一般来说,JSON 的格式比 XML 格式使用得更广泛,但有些时候,你可能没有办法,比如微信公众号的 API 就有不少地方是基于 XML 的 (2019-06)。

Web 框架

说道 Web 框架,Python 世界内,大家一般会想到 Django (D 不发音) 或 Flask,不少人会喜欢 Django,因为它什么都提供了,而我更喜欢 Flask,因为什么都提供了反倒束缚了手脚,局限了思维。

watchdog

监控本地系统文件变化的工具。但也并不完全可靠,默认的引擎,依赖于系统的自身支持 (这样性能才高,文件变动之后能直接在代码中捕获 event ),可能跟 Dropbox 此类软件的同步扫描产生冲突。
watchdog 主要用于客户端软件中,服务端不大可能拿来使用。一般来说,真正的产品场景中,还需要准备一个效率更低的、全遍历式的备用计划,同时在后面运行着。