标准库¶
Python 总是“开箱即用”。这意味着在标准的 CPython 发行版中, 有一些用于处理文件、线程、网络、网站、音乐、键盘、屏幕、文本的库,以及各种实用程序的库。
CPython 附带的库中,有一些对所有东西都很有用,比如 collections
模块和 sys
模块。
另外一些比较模糊,你永远不知道什么时候会派上用场。
CPython 标准库中有两种类型的模块:
那些用纯 Python 编写的实用程序库;
那些使用 Python 包装器的用 C 编写的库。
本章中探索这两种类型。
Python 模块¶
纯 Python 编写的模块都位于源代码中的 Lib
目录中。
一些较大的模块在子文件夹中有子模块,例如 email
模块。
一个容易查看的模块是 colorsys
模块。它只有几百行 Python 代码。
colorsys
模块有一些用于转换色阶的实用函数。
从源代码安装 Python 发行版时,标准库模块会从 Lib
文件夹复制到发行版文件夹中。
当启动 Python 时,此文件夹始终是你路径的一部分,因此你可以导入模块而无需担心它们的位置。
例如:
>>> import colorsys
>>> colorsys
<module 'colorsys' from '/usr/shared/lib/python3.7/colorsys.py'>
>>> colorsys.rgb_to_hls(255,0,0) (0.0, 127.5, -1.007905138339921)
我们可以在 Lib/colorsys.py
里面看到 rgb_to_hls()
的源码:
# HLS: Hue, Luminance, Saturation
# H: position in the spectrum
# L: color lightness
# S: color saturation
def rgb_to_hls(r, g, b):
maxc = max(r, g, b)
minc = min(r, g, b)
# XXX Can optimize (maxc+minc) and (maxc-minc) l = (minc+maxc)/2.0
if minc == maxc:
return 0.0, l, 0.0
if l <= 0.5:
s = (maxc-minc) / (maxc+minc)
else:
s = (maxc-minc) / (2.0-maxc-minc)
rc = (maxc-r) / (maxc-minc)
gc = (maxc-g) / (maxc-minc)
bc = (maxc-b) / (maxc-minc)
if r == maxc:
h = bc-gc
elif g == maxc:
h = 2.0+rc-bc
else:
h = 4.0+gc-rc
h = (h/6.0) % 1.0
return h, l, s
这个函数没有什么特别之处,它只是标准的 Python。你会发现所有纯 Python 标准库模块都有类似的东西。 它们只是用简单的 Python 编写的,布局合理且易于理解。 你甚至可以发现改进或错误,因此你可以对它们进行更改并将其贡献给 Python 发行版。 我们将在本书结尾处介绍这一点。
Python 和 C 模块¶
其余模块是用 C 编写的,或者是 Python 和 C 的组合。
这些的源代码中,Python 组件在 Lib
中,C 组件在 Modules
中。
此规则有两个例外,sys
模块在 Python/sysmodule.c
中,
__builtins__
模块在 Python/bltinmodule.c
中。
当解释器被实例化时,Python 将 import * from __builtins__
,因此所有的函数,
如 print()
、chr()
、format()
等,都可以在 Python/bltinmodule.c
中找到。
因为 sys
模块非常特定于解释器和 CPython 的内部结构,所以可以在 Python
目录中找到。
它也被标记为 CPython 的“实现细节”,在其他发行版中找不到。
内置的 print()
函数可能是你在 Python 中学会的第一件事。
那么当你输入 print("hello world!")
时会发生什么?
参数
"hello world"
被编译器从字符串常量转换为PyUnicodeObject
;builtin_print()
使用 1 个参数执行,并且kwnames
为 NULL;file
变量设置为PyId_stdout
,即系统的stdout
句柄;每个参数将发送给
file
;一个换行符号
\n
将被发送给file
。
Python/bltinmodule.c
第1828行:
static PyObject *
builtin_print(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
{
...
if (file == NULL || file == Py_None) {
file = _PySys_GetObjectId(&PyId_stdout);
...
}
...
for (i = 0; i < nargs; i++) {
if (i > 0) {
if (sep == NULL)
err = PyFile_WriteString(" ", file);
else
err = PyFile_WriteObject(sep, file,
Py_PRINT_RAW);
if (err)
return NULL;
}
err = PyFile_WriteObject(args[i], file, Py_PRINT_RAW);
if (err)
return NULL;
}
if (end == NULL)
err = PyFile_WriteString("\n", file);
else
err = PyFile_WriteObject(end, file, Py_PRINT_RAW);
...
Py_RETURN_NONE;
}
一些用 C 编写的模块的内容使用了操作系统函数。 由于 CPython 源代码需要编译到 macOS、Windows、Linux 和其他基于 *nix 的操作系统,因此存在一些特殊情况。
time
模块就是一个很好的例子。 Windows 在操作系统中保存和存储时间的方式与 Linux 和 macOS 有着根本的不同。
这是 操作系统之间 时钟功能精度不同的原因之一。
在 Modules/timemodule.c
中,基于 Unix 系统的操作系统时间函数是从 <sys/times.h>
导入的:
#ifdef HAVE_SYS_TIMES_H
#include <sys/times.h>
#endif
...
#ifdef MS_WINDOWS
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include "pythread.h"
#endif /* MS_WINDOWS */
...
在文件的后面,time_process_time_ns()
被定义为 _PyTime_GetProcessTimeWithInfo()
的包装器:
static PyObject *
time_process_time_ns(PyObject *self, PyObject *unused)
{
_PyTime_t t;
if (_PyTime_GetProcessTimeWithInfo(&t, NULL) < 0) {
return NULL;
}
return _PyTime_AsNanosecondsObject(t);
}
_PyTime_GetProcessTimeWithInfo()
在源代码中有多种不同的实现方式,但根据操作系统的不同,
只有某些部分被编译成模块的二进制文件。
Windows 系统将调用 GetProcessTimes()
,Unix 系统将调用 clock_gettime()
。
对同一 API 具有多个实现的其他模块是 threading 模块、文件系统模块和网络模块。 由于操作系统的行为不同,CPython 源代码尽可能地实现相同的行为,并暴露一致的抽象 API。