Глава 12. Стандартная библиотека

Python всегда поставляется вместе с батарейками. Это означает, что стандартный дистрибутив Python содержит библиотеки для работы с файлами, потоками, вебсайтами, музыкой, клавиатурой, экранами, текстом и целым диапазоном утилит.

Некоторые из поставляемых совместно с CPython батареек подобны батарейкам АА и полезны практически при любой ситуации. В качестве примеров можно привести модули collections и sys. Но некоторые из них слегка менее известны, превращая их в некое подобие батареек от наручных часов: вы никогда не знаете наперёд когда они вам понадобятся.

Имеются два типа модулей в стандартной библиотеке CPython:

  1. Те, кторые написаны на чистом Python и которые предоставляют утилиты

  2. Те, которые написаны на C с обёрткой из Python

В данной главе мы изучим оба этих типа.

Модули Python

Все те модули, которые написаны на чистом Python, располагаются в каталоге Lib исходного кода. Некоторые более старшие модули обладают подчинёнными модулями в подчинённых папках, такие как модуль email.

Некий простой модуль, с которым вы могли не сталкиваться ранее это модуль colorsys. Он обладает сотней строк кода Python и содержит некие функции утилит для преобразования цветовых шкал.

Когда вы устанавливаете дистрибутив Python из исходного кода, модули стандартной библиотеки копируются из папки Lib в папку своего дистрибутива. Эта папка всегда выступает частью вашего пути при запуске Python, поэтому вы способны выполнять import таких модулей не беспокоясь относительно их местоположения.

Например, вот как импортировать и применять colorsys:


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

Вы можете обнаружить исходный код rgb_to_hls() внутри Lib/colorsys.py:


# 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 или на комбинации Pyton и C. Их исходный код расположен в Lib для компонентов Pyton и в Modules для компонентов C. Имеются два исключения:

  1. Модуль sys находится в Python/sysmodule.c

  2. А модуль __builtins__ обнаруживается в Python/bltinmodule.c

Поскольку этот модуль sys настолько специфичен для своего интерпретатора и внутреннего устройства CPython, он обнаруживается в самом каталоге Python. Он также помечается в качестве "подробности реализации" CPythonи не находится в прочих дистрибутивах.

Python будет import * from __builtins__ при конкретизации интерпретатора, поэтому все такие встроенные функции, как print(), chr(), format() и тому подобные обнаруживаются внутри Python/bltinmodule.c.

Встроенная функция print() вероятно была самой первой изученной вами для применения в Python. Итак, что в точности происходит когда вы набираете print("Hello, World")?

Вот разбивка:

  1. Наш компилятор преобразовывает полученный аргумент "Hello, World" из строковой константы в PyUnicodeObject.

  2. builtin_print() исполняется с одним аргументом и NULL kwnames.

  3. Значение переменной file устанавливается в PyId_stdout, системный обработчик stdout.

  4. Все аргументы отправляются в file.

  5. В file отправляется разрыв строки (\n).

Вот как это работает, строка 1828 Python/bltinmodule.c :


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, выставляющем функции операционной системы. Поскольку исходный код CPyton необходимо компилировать в to 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 являются модули построения потоков. Поскольку операционные системы ведут себя по разному, сам исходный код CPython реализует в точности то поведение, которое подходит наилучшим образом и выставляет его применяя совместимый, абстрагированный API.