
- •Об авторе
- •О группе редакторов
- •Предисловие
- •Введение
- •Как использовать эту книгу
- •Загрузка исходного кода CPython
- •Что в исходном коде?
- •Настройка среды разработки
- •IDE или редактор?
- •Настройка Visual Studio
- •Настройка Visual Studio Code
- •Настройка Vim
- •Выводы
- •Компиляция CPython
- •Компиляция CPython на macOS
- •Компиляция CPython на Linux
- •Установка специализированной версии
- •Знакомство с Make
- •Make-цели CPython
- •Компиляция CPython на Windows
- •Профильная оптимизация
- •Выводы
- •Грамматика и язык Python
- •Спецификация языка Python
- •Генератор парсеров
- •Повторное генерирование грамматики
- •Выводы
- •Конфигурация и ввод
- •Конфигурация состояния
- •Структура данных конфигурации среды выполнения
- •Конфигурация сборки
- •Сборка модуля из входных данных
- •Выводы
- •Генерирование конкретного синтаксического дерева
- •Парсер/токенизатор CPython
- •Абстрактные синтаксические деревья
- •Важные термины
- •Пример: добавление оператора «почти равно»
- •Выводы
- •Компилятор
- •Исходные файлы
- •Важные термины
- •Создание экземпляра компилятора
- •Флаги будущей функциональности и флаги компилятора
- •Таблицы символических имен
- •Основная компиляция
- •Ассемблер
- •Создание объекта кода
- •Использование Instaviz для вывода объекта кода
- •Пример: реализация оператора «почти равно»
- •Выводы
- •Цикл вычисления
- •Исходные файлы
- •Важные термины
- •Построение состояния потока
- •Построение объектов кадров
- •Выполнение кадра
- •Стек значений
- •Пример: добавление элемента в список
- •Выводы
- •Управление памятью
- •Выделение памяти в C
- •Проектирование системы управления памятью Python
- •Аллокаторы памяти CPython
- •Область выделения объектной памяти и PyMem
- •Область выделения сырой памяти
- •Нестандартные области выделения памяти
- •Санитайзеры выделенной памяти
- •Арена памяти PyArena
- •Подсчет ссылок
- •Сборка мусора
- •Выводы
- •Параллелизм и конкурентность
- •Модели параллелизма и конкурентности
- •Структура процесса
- •Многопроцессорный параллелизм
- •Многопоточность
- •Асинхронное программирование
- •Генераторы
- •Сопрограммы
- •Асинхронные генераторы
- •Субинтерпретаторы
- •Выводы
- •Объекты и типы
- •Примеры этой главы
- •Встроенные типы
- •Типы объектов
- •Тип type
- •Типы bool и long
- •Тип строки Юникода
- •Словари
- •Выводы
- •Стандартная библиотека
- •Модули Python
- •Модули Python и C
- •Набор тестов
- •Запуск набора тестов в Windows
- •Запуск набора тестов в Linux или macOS
- •Флаги тестирования
- •Запуск конкретных тестов
- •Модули тестирования
- •Вспомогательные средства тестирования
- •Выводы
- •Отладка
- •Обработчик сбоев
- •Компиляция поддержки отладки
- •LLDB для macOS
- •Отладчик Visual Studio
- •Отладчик CLion
- •Выводы
- •Бенчмаркинг, профилирование и трассировка
- •Использование timeit для микробенчмарка
- •Использование набора тестов производительности Python
- •Профилирование кода Python с использованием cProfile
- •Выводы
- •Что дальше?
- •Создание расширений C для CPython
- •Улучшение приложений Python
- •Участие в проекте CPython
- •Дальнейшее обучение
- •Препроцессор C
- •Базовый синтаксис C
- •Выводы
- •Благодарности

268 Объекты и типы
PyObject_GenericGetDict() реализует логику получения экземпляра словаря для заданного объекта. PyObject_GetAttr()выполняет реализацию __getattr__ () по умолчанию, а PyObject_SetAttr() реализует __setattr__().
ТИПЫ BOOL И LONG
Тип bool — самая прямолинейная реализация встроенных типов. Он наследуется от long и содержит предопределенные константы Py_True и Py_False. Эти константы являются неизменяемыми экземплярами, создаваемыми при инстанцировании интерпретатора Python.
Внутри Objects boolobject.c находится вспомогательная функция для создания экземпляра bool из числа:
Objects boolobject.c, строка 28
PyObject *PyBool_FromLong(long ok)
{
PyObject *result;
if (ok)
result = Py_True;
else
result = Py_False; Py_INCREF(result); return result;
}
Эта функция использует C для вычисления числового типа, чтобы присвоить result значение Py_True или Py_False и увеличить счетчик ссылок.
Числовые функции для and, xor и or реализованы, но операции сложения, вычитания и деления заблокированы из базового типа long, так как деление двух логических значений не имеет смысла.
Реализация and для значения bool построена так, что сначала проверяет, являются ли a и b логическими типами. Если условие не выполняется, значения преобразуются в числа и операция выполняется с двумя числами:
Objects boolobject.c, строка 61
static PyObject * bool_and(PyObject *a, PyObject *b)
{
Книги для программистов: https://t.me/booksforits

Типы bool и long 269
if (!PyBool_Check(a) || !PyBool_Check(b))
return PyLong_Type.tp_as_number->nb_and(a, b); return PyBool_FromLong((a == Py_True) & (b == Py_True));
}
Тип long
Тип long немного сложнее bool. При переходе от Python 2 к Python 3 CPython перестал поддерживать тип int, и основным целочисленным типом стал long.
Тип long в Python отличается тем, что он способен хранить значение вариативной (изменяемой) длины. Максимальная длина задается в скомпилированном двоичном файле.
Структура данных long в Python состоит из заголовка переменной PyObject и списка цифр. Список цифр ob_digit изначально состоит из одной цифры, но при инициализации расширяется:
Include longintrepr.h, строка 85
struct _longobject {
PyObject_VAR_HEAD digit ob_digit[1];
};
Например, числу 1 будет соответствовать ob_digit [1], а числу 24 601 — ob_digit [2, 4, 6, 0, 1].
Память для нового значения long выделяется вызовом _PyLong_New(). Эта функция получает фиксированную длину и проверяет, что она меньше MAX_ LONG_DIGITS. Затем память для ob_digit выделяется заново в соответствии с длиной.
Чтобы преобразовать тип long языка C в тип long языка Python, C long преобразуется в список цифр, выделяется память для Python long, после чего задается значение каждой цифры.
Для чисел из одной цифры объект long инициализируется с ob_digit, уже имеющим длину 1. Затем значение задается без выделения памяти:
Objects longobject.c, строка 297
PyObject * PyLong_FromLong(long ival)
{
Книги для программистов: https://t.me/booksforits

270 Объекты и типы
PyLongObject *v; unsigned long abs_ival;
unsigned long t; /* unsigned, поэтому >> не распространяет бит знака */ int ndigits = 0;
int sign;
CHECK_SMALL_INT(ival);
...
/* Быстрая ветвь для целых чисел из одной цифры */ if (!(abs_ival >> PyLong_SHIFT)) {
v = _PyLong_New(1); if (v) {
Py_SIZE(v) = sign;
v->ob_digit[0] = Py_SAFE_DOWNCAST( abs_ival, unsigned long, digit);
}
return (PyObject*)v;
}
...
/* Большие числа: цикл для определения количества цифр */ t = abs_ival;
while (t) { ++ndigits;
t >>= PyLong_SHIFT;
}
v = _PyLong_New(ndigits); if (v != NULL) {
digit *p = v->ob_digit;
Py_SIZE(v) = ndigits*sign; t = abs_ival;
while (t) {
*p++ = Py_SAFE_DOWNCAST(
t & PyLong_MASK, unsigned long, digit); t >>= PyLong_SHIFT;
}
}
return (PyObject *)v;
}
Преобразование числа с плавающей точкой double в Python long осуществляется вызовом PyLong_FromDouble().
Другие функции реализации в Objects longobject.c содержат вспомогательные функции, например функцию PyLong_FromUnicodeObject() для преобразования строки Юникода в число.
Книги для программистов: https://t.me/booksforits

Типы bool и long 271
Пример
Слот типа расширенного сравнения для long заполняется long_richcompare(). Эта функция является оберткой для long_compare():
Objects longobject.c, строка 3031
static PyObject *
long_richcompare(PyObject *self, PyObject *other, int op)
{
Py_ssize_t result;
CHECK_BINOP(self, other); if (self == other)
result = 0;
else
result = long_compare((PyLongObject*)self, (PyLongObject*)other);
Py_RETURN_RICHCOMPARE(result, 0, op);
}
Функция long_compare() сначала проверяет длину (количество цифр) двух переменных: a и b. Если длины совпадают, то она перебирает в цикле все цифры и проверяет, равны ли они друг другу.
long_compare() возвращает один из трех видов значений:
1.Если a < b, возвращается отрицательное число.
2.Если a == b, возвращается 0.
3.Если a > b, возвращается положительное число.
Например, при выполнении условия 1 == 5 результат будет равен -4. Для 5 == 1 результат равен 4.
Можно добавить следующий блок кода перед макросом Py_RETURN_RICHCOMPARE, чтобы он возвращал True, если абсолютное значение результата <=1. Макрос Py_ABS()возвращает абсолютное целое значение со знаком:
if (op == Py_AlE) {
if (Py_ABS(result) <= 1) Py_RETURN_TRUE;
else
Py_RETURN_FALSE;
}
Py_RETURN_RICHCOMPARE(result, 0, op);
}
Книги для программистов: https://t.me/booksforits