Документация Python неофициальный перевод

newtypes_tutorial.md

1391 строк · 69.5 КБ · обычная страница · сырой текст · скачать

1> **Источник:** https://python-all.ru/3.7/extending/newtypes_tutorial.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# 2. Определение типов расширений: учебное пособие89Python позволяет разработчику модуля расширения на C определять новые типы, которыми можно манипулировать из кода Python, во многом как встроенные типы [`str`](https://python-all.ru/3.7/library/stdtypes.html#str) и [`list`](https://python-all.ru/3.7/library/stdtypes.html#list). Код для всех типов расширений следует определённому шаблону, но есть некоторые детали, которые необходимо понять, прежде чем приступить к работе. Этот документ является вводным руководством по данной теме.1011## 2.1. Основы1213Среда выполнения [CPython](https://python-all.ru/3.7/glossary.html#term-cpython) рассматривает все объекты Python как переменные типа [`PyObject*`](https://python-all.ru/3.7/c-api/structures.html#c.PyObject), который служит «базовым типом» для всех объектов Python. Сама структура [`PyObject`](https://python-all.ru/3.7/c-api/structures.html#c.PyObject) содержит только [счётчик ссылок](https://python-all.ru/3.7/glossary.html#term-reference-count) объекта и указатель на «объект типа» объекта. В этом и заключается суть: объект типа определяет, какие (C) функции вызываются интерпретатором, когда, например, выполняется поиск атрибута объекта, вызов метода или его умножение на другой объект. Эти C-функции называются «методами типа».1415Итак, чтобы определить новый тип расширения, необходимо создать новый объект типа.1617Такие вещи можно объяснить только на примере, поэтому вот минимальный, но полный модуль, определяющий новый тип с именем `Custom` внутри C-модуля расширения `custom`:1819> **Примечание**20>21> То, что мы показываем здесь, – это традиционный способ определения *статических* типов расширений. Он должен быть достаточным для большинства случаев. C API также позволяет определять типы расширений, размещаемые в куче, с помощью функции [`PyType_FromSpec()`](https://python-all.ru/3.7/c-api/type.html#c.PyType_FromSpec), которая не рассматривается в этом руководстве.2223```c24#define PY_SSIZE_T_CLEAN25#include <Python.h>2627typedef struct {28    PyObject_HEAD29    /* Сюда помещаются поля, специфичные для типа. */30} CustomObject;3132static PyTypeObject CustomType = {33    PyVarObject_HEAD_INIT(NULL, 0)34    .tp_name = "custom.Custom",35    .tp_doc = "Custom objects",36    .tp_basicsize = sizeof(CustomObject),37    .tp_itemsize = 0,38    .tp_flags = Py_TPFLAGS_DEFAULT,39    .tp_new = PyType_GenericNew,40};4142static PyModuleDef custommodule = {43    PyModuleDef_HEAD_INIT,44    .m_name = "custom",45    .m_doc = "Example module that creates an extension type.",46    .m_size = -1,47};4849PyMODINIT_FUNC50PyInit_custom(void)51{52    PyObject *m;53    if (PyType_Ready(&CustomType) < 0)54        return NULL;5556    m = PyModule_Create(&custommodule);57    if (m == NULL)58        return NULL;5960    Py_INCREF(&CustomType);61    if (PyModule_AddObject(m, "Custom", (PyObject *) &CustomType) < 0) {62        Py_DECREF(&CustomType);63        Py_DECREF(m);64        return NULL;65    }6667    return m;68}69```7071Это довольно много для одномоментного восприятия, но, надеюсь, некоторые части покажутся знакомыми из предыдущей главы. Этот файл определяет три вещи:72731. Что содержит `Custom` **объект**: это структура `CustomObject`, которая выделяется один раз для каждого экземпляра `Custom`.742. Как ведёт себя `Custom` **тип**: это структура `CustomType`, которая определяет набор флагов и указателей на функции, которые интерпретатор проверяет при запросе определённых операций.753. Как инициализировать модуль `custom`: это `PyInit_custom` функция и связанная с ней структура `custommodule`.7677Первая часть:7879```c80typedef struct {81    PyObject_HEAD82} CustomObject;83```8485Вот что будет содержать пользовательский объект. `PyObject_HEAD` является обязательным в начале каждой структуры объекта и определяет поле `ob_base` типа [`PyObject`](https://python-all.ru/3.7/c-api/structures.html#c.PyObject), содержащее указатель на объект типа и счётчик ссылок (доступ к ним можно получить с помощью макросов [`Py_REFCNT`](https://python-all.ru/3.7/c-api/structures.html#c.Py_REFCNT) и [`Py_TYPE`](https://python-all.ru/3.7/c-api/structures.html#c.Py_TYPE) соответственно). Причина использования макроса – абстрагирование от компоновки и возможность добавления дополнительных полей в отладочных сборках.8687> **Примечание**88>89> В приведённом выше коде нет точки с запятой после макроса [`PyObject_HEAD`](https://python-all.ru/3.7/c-api/structures.html#c.PyObject_HEAD). Будьте осторожны, чтобы случайно её не добавить: некоторые компиляторы выдадут ошибку.9091Конечно, объекты обычно хранят дополнительные данные помимо стандартного шаблона `PyObject_HEAD`; например, вот определение для стандартных чисел с плавающей запятой Python:9293```c94typedef struct {95    PyObject_HEAD96    double ob_fval;97} PyFloatObject;98```99100Вторая часть – это определение объекта типа.101102```c103static PyTypeObject CustomType = {104    PyVarObject_HEAD_INIT(NULL, 0)105    .tp_name = "custom.Custom",106    .tp_doc = "Custom objects",107    .tp_basicsize = sizeof(CustomObject),108    .tp_itemsize = 0,109    .tp_flags = Py_TPFLAGS_DEFAULT,110    .tp_new = PyType_GenericNew,111};112```113114> **Примечание**115>116> Рекомендуется использовать именованные инициализаторы в стиле C99, как указано выше, чтобы не перечислять все поля [`PyTypeObject`](https://python-all.ru/3.7/c-api/type.html#c.PyTypeObject), которые не важны, а также не беспокоиться о порядке объявления полей.117118Фактическое определение [`PyTypeObject`](https://python-all.ru/3.7/c-api/type.html#c.PyTypeObject) в `object.h` содержит гораздо больше [полей](https://python-all.ru/3.7/c-api/typeobj.html#type-structs), чем приведённое выше определение. Оставшиеся поля будут заполнены нулями компилятором C, и обычно принято не указывать их явно, если они не нужны.119120Мы разберём его, поле за полем:121122```c123PyVarObject_HEAD_INIT(NULL, 0)124```125126Эта строка является обязательным шаблоном для инициализации поля `ob_base`, упомянутого выше.127128```c129.tp_name = "custom.Custom",130```131132Имя нашего типа. Оно будет отображаться в стандартном текстовом представлении наших объектов и в некоторых сообщениях об ошибках, например:133134```pycon135>>> "" + custom.Custom()136Traceback (most recent call last):137  File "<stdin>", line 1, in <module>138TypeError: can only concatenate str (not "custom.Custom") to str139```140141Обратите внимание, что имя является составным (с точками) и включает как имя модуля, так и имя типа внутри модуля. В данном случае модуль – `custom`, а тип – `Custom`, поэтому мы устанавливаем имя типа `custom.Custom`. Использование реального составного пути импорта важно для совместимости вашего типа с модулями [`pydoc`](https://python-all.ru/3.7/library/pydoc.html#module-pydoc) и [`pickle`](https://python-all.ru/3.7/library/pickle.html#module-pickle).142143```c144.tp_basicsize = sizeof(CustomObject),145.tp_itemsize = 0,146```147148Это нужно, чтобы Python знал, сколько памяти выделять при создании новых экземпляров `Custom`. [`tp_itemsize`](https://python-all.ru/3.7/c-api/typeobj.html#c.PyTypeObject.tp_itemsize) используется только для объектов переменного размера, в остальных случаях должно быть равно нулю.149150> **Примечание**151>152> Если вы хотите, чтобы ваш тип можно было наследовать из Python, и ваш тип имеет тот же [`tp_basicsize`](https://python-all.ru/3.7/c-api/typeobj.html#c.PyTypeObject.tp_basicsize), что и его базовый тип, у вас могут возникнуть проблемы с множественным наследованием. Подкласс вашего типа на Python должен будет указать ваш тип первым в своём [`__bases__`](https://python-all.ru/3.7/library/stdtypes.html#class.__bases__), иначе он не сможет вызвать метод [`__new__()`](https://python-all.ru/3.7/reference/datamodel.html#object.__new__) вашего типа без ошибки. Вы можете избежать этой проблемы, обеспечив большее значение для [`tp_basicsize`](https://python-all.ru/3.7/c-api/typeobj.html#c.PyTypeObject.tp_basicsize) вашего типа по сравнению с его базовым типом. В большинстве случаев это будет верно, потому что либо ваш базовый тип будет [`object`](https://python-all.ru/3.7/library/functions.html#object), либо вы будете добавлять члены данных в базовый тип, тем самым увеличивая его размер.153154Мы устанавливаем флаги класса в [`Py_TPFLAGS_DEFAULT`](https://python-all.ru/3.7/c-api/typeobj.html#Py_TPFLAGS_DEFAULT).155156```c157.tp_flags = Py_TPFLAGS_DEFAULT,158```159160Все типы должны включать эту константу в свои флаги. Она включает все члены, определённые до Python 3.3 как минимум. Если вам нужны дополнительные члены, вам потребуется выполнить OR с соответствующими флагами.161162Мы предоставляем строку документации для типа в [`tp_doc`](https://python-all.ru/3.7/c-api/typeobj.html#c.PyTypeObject.tp_doc).163164```c165.tp_doc = "Custom objects",166```167168Чтобы разрешить создание объектов, мы должны предоставить обработчик [`tp_new`](https://python-all.ru/3.7/c-api/typeobj.html#c.PyTypeObject.tp_new). Это эквивалент метода Python [`__new__()`](https://python-all.ru/3.7/reference/datamodel.html#object.__new__), но должен быть указан явно. В данном случае мы можем просто использовать реализацию по умолчанию, предоставляемую API-функцией [`PyType_GenericNew()`](https://python-all.ru/3.7/c-api/type.html#c.PyType_GenericNew).169170```c171.tp_new = PyType_GenericNew,172```173174Всё остальное в файле должно быть знакомым, за исключением некоторого кода в `PyInit_custom()`:175176```c177if (PyType_Ready(&CustomType) < 0)178    return;179```180181Это инициализирует тип `Custom`, заполняя ряд членов подходящими значениями по умолчанию, включая `ob_type`, который мы изначально установили в `NULL`.182183```c184Py_INCREF(&CustomType);185if (PyModule_AddObject(m, "Custom", (PyObject *) &CustomType) < 0) {186    Py_DECREF(&CustomType);187    Py_DECREF(m);188    return NULL;189}190```191192Это добавляет тип в словарь модуля. Это позволяет нам создавать экземпляры `Custom` вызовом класса `Custom`:193194```pycon195>>> import custom196>>> mycustom = custom.Custom()197```198199Вот и всё! Осталось только собрать его; поместите приведённый выше код в файл с именем `custom.c` и:200201```python202from distutils.core import setup, Extension203setup(name="custom", version="1.0",204      ext_modules=[Extension("custom", ["custom.c"])])205```206207в файл с именем `setup.py`; затем введите208209```console210$ python setup.py build211```212213в командной оболочке должен создать файл `custom.so` в подкаталоге; перейдите в этот каталог и запустите Python – вы сможете выполнить `import custom` и поработать с объектами Custom.214215Было не так сложно, правда?216217Конечно, текущий тип Custom довольно неинтересен. У него нет данных, и он ничего не делает. Его даже нельзя унаследовать.218219> **Примечание**220>221> Хотя в этой документации демонстрируется стандартный модуль [`distutils`](https://python-all.ru/3.7/library/distutils.html#module-distutils) для создания расширений на C, в реальных проектах рекомендуется использовать более новую и лучше поддерживаемую библиотеку `setuptools`. Документация о том, как это сделать, выходит за рамки данного документа и находится в [Руководстве пользователя по упаковке Python](https://python-all.ru/3.7/extending/newtypes_tutorial.html).222223## 2.2. Добавление данных и методов к базовому примеру224225Давайте расширим базовый пример, добавив некоторые данные и методы. Также сделаем тип пригодным для использования в качестве базового класса. Мы создадим новый модуль `custom2`, который добавляет эти возможности:226227```c228#define PY_SSIZE_T_CLEAN229#include <Python.h>230#include "structmember.h"231232typedef struct {233    PyObject_HEAD234    PyObject *first; /* имя */235    PyObject *last;  /* фамилия */236    int number;237} CustomObject;238239static void240Custom_dealloc(CustomObject *self)241{242    Py_XDECREF(self->first);243    Py_XDECREF(self->last);244    Py_TYPE(self)->tp_free((PyObject *) self);245}246247static PyObject *248Custom_new(PyTypeObject *type, PyObject *args, PyObject *kwds)249{250    CustomObject *self;251    self = (CustomObject *) type->tp_alloc(type, 0);252    if (self != NULL) {253        self->first = PyUnicode_FromString("");254        if (self->first == NULL) {255            Py_DECREF(self);256            return NULL;257        }258        self->last = PyUnicode_FromString("");259        if (self->last == NULL) {260            Py_DECREF(self);261            return NULL;262        }263        self->number = 0;264    }265    return (PyObject *) self;266}267268static int269Custom_init(CustomObject *self, PyObject *args, PyObject *kwds)270{271    static char *kwlist[] = {"first", "last", "number", NULL};272    PyObject *first = NULL, *last = NULL, *tmp;273274    if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOi", kwlist,275                                     &first, &last,276                                     &self->number))277        return -1;278279    if (first) {280        tmp = self->first;281        Py_INCREF(first);282        self->first = first;283        Py_XDECREF(tmp);284    }285    if (last) {286        tmp = self->last;287        Py_INCREF(last);288        self->last = last;289        Py_XDECREF(tmp);290    }291    return 0;292}293294static PyMemberDef Custom_members[] = {295    {"first", T_OBJECT_EX, offsetof(CustomObject, first), 0,296     "first name"},297    {"last", T_OBJECT_EX, offsetof(CustomObject, last), 0,298     "last name"},299    {"number", T_INT, offsetof(CustomObject, number), 0,300     "custom number"},301    {NULL}  /* Страж */302};303304static PyObject *305Custom_name(CustomObject *self, PyObject *Py_UNUSED(ignored))306{307    if (self->first == NULL) {308        PyErr_SetString(PyExc_AttributeError, "first");309        return NULL;310    }311    if (self->last == NULL) {312        PyErr_SetString(PyExc_AttributeError, "last");313        return NULL;314    }315    return PyUnicode_FromFormat("%S %S", self->first, self->last);316}317318static PyMethodDef Custom_methods[] = {319    {"name", (PyCFunction) Custom_name, METH_NOARGS,320     "Return the name, combining the first and last name"321    },322    {NULL}  /* Страж */323};324325static PyTypeObject CustomType = {326    PyVarObject_HEAD_INIT(NULL, 0)327    .tp_name = "custom2.Custom",328    .tp_doc = "Custom objects",329    .tp_basicsize = sizeof(CustomObject),330    .tp_itemsize = 0,331    .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,332    .tp_new = Custom_new,333    .tp_init = (initproc) Custom_init,334    .tp_dealloc = (destructor) Custom_dealloc,335    .tp_members = Custom_members,336    .tp_methods = Custom_methods,337};338339static PyModuleDef custommodule = {340    PyModuleDef_HEAD_INIT,341    .m_name = "custom2",342    .m_doc = "Example module that creates an extension type.",343    .m_size = -1,344};345346PyMODINIT_FUNC347PyInit_custom2(void)348{349    PyObject *m;350    if (PyType_Ready(&CustomType) < 0)351        return NULL;352353    m = PyModule_Create(&custommodule);354    if (m == NULL)355        return NULL;356357    Py_INCREF(&CustomType);358    if (PyModule_AddObject(m, "Custom", (PyObject *) &CustomType) < 0) {359        Py_DECREF(&CustomType);360        Py_DECREF(m);361        return NULL;362    }363364    return m;365}366```367368Эта версия модуля содержит ряд изменений.369370Мы добавили дополнительный include:371372```c373#include <structmember.h>374```375376Этот include предоставляет объявления, которые мы используем для обработки атрибутов, как описано немного позже.377378Тип `Custom` теперь имеет три атрибута данных в своей C-структуре: *first*, *last* и *number*. Переменные *first* и *last* являются строками Python, содержащими имя и фамилию. Атрибут *number* – это целое число C.379380Структура объекта обновляется соответствующим образом:381382```c383typedef struct {384    PyObject_HEAD385    PyObject *first; /* имя */386    PyObject *last;  /* фамилия */387    int number;388} CustomObject;389```390391Поскольку теперь у нас есть данные для управления, мы должны быть более внимательны к выделению и освобождению памяти объектов. Как минимум, нам нужен метод освобождения:392393```c394static void395Custom_dealloc(CustomObject *self)396{397    Py_XDECREF(self->first);398    Py_XDECREF(self->last);399    Py_TYPE(self)->tp_free((PyObject *) self);400}401```402403который присваивается члену [`tp_dealloc`](https://python-all.ru/3.7/c-api/typeobj.html#c.PyTypeObject.tp_dealloc):404405```c406.tp_dealloc = (destructor) Custom_dealloc,407```408409Этот метод сначала сбрасывает счётчики ссылок двух атрибутов Python. [`Py_XDECREF()`](https://python-all.ru/3.7/c-api/refcounting.html#c.Py_XDECREF) корректно обрабатывает случай, когда его аргумент равен `NULL` (что может произойти, если `tp_new` не выполнился до конца). Затем он вызывает член [`tp_free`](https://python-all.ru/3.7/c-api/typeobj.html#c.PyTypeObject.tp_free) типа объекта (вычисленный с помощью `Py_TYPE(self)`) для освобождения памяти объекта. Обратите внимание, что тип объекта может не быть `CustomType`, поскольку объект может быть экземпляром подкласса.410411> **Примечание**412>413> Явное приведение к `destructor` выше необходимо, потому что мы определили `Custom_dealloc` как принимающий аргумент `CustomObject *`, но указатель на функцию `tp_dealloc` ожидает получения аргумента `PyObject *`. В противном случае компилятор выдаст предупреждение. Это объектно-ориентированный полиморфизм в C!414415Мы хотим убедиться, что имя и фамилия инициализируются пустыми строками, поэтому мы предоставляем реализацию `tp_new`:416417```c418static PyObject *419Custom_new(PyTypeObject *type, PyObject *args, PyObject *kwds)420{421    CustomObject *self;422    self = (CustomObject *) type->tp_alloc(type, 0);423    if (self != NULL) {424        self->first = PyUnicode_FromString("");425        if (self->first == NULL) {426            Py_DECREF(self);427            return NULL;428        }429        self->last = PyUnicode_FromString("");430        if (self->last == NULL) {431            Py_DECREF(self);432            return NULL;433        }434        self->number = 0;435    }436    return (PyObject *) self;437}438```439440и устанавливаем его в член [`tp_new`](https://python-all.ru/3.7/c-api/typeobj.html#c.PyTypeObject.tp_new):441442```c443.tp_new = Custom_new,444```445446Обработчик `tp_new` отвечает за создание (в отличие от инициализации) объектов типа. Он предоставляется в Python как метод [`__new__()`](https://python-all.ru/3.7/reference/datamodel.html#object.__new__). Не требуется определять член `tp_new`, и действительно, многие расширения типов просто повторно используют [`PyType_GenericNew()`](https://python-all.ru/3.7/c-api/type.html#c.PyType_GenericNew), как это сделано в первой версии типа `Custom` выше. В этом случае мы используем обработчик `tp_new` для инициализации атрибутов `first` и `last` значениями по умолчанию, отличными от `NULL`.447448`tp_new` передаётся тип, экземпляр которого создаётся (не обязательно `CustomType`, если создаётся экземпляр подкласса), и любые аргументы, переданные при вызове типа; ожидается, что он вернёт созданный экземпляр. Обработчики `tp_new` всегда принимают позиционные и ключевые аргументы, но часто игнорируют их, оставляя обработку аргументов методам-инициализаторам (также известным как `tp_init` в C или `__init__` в Python).449450> **Примечание**451>452> `tp_new` не должен вызывать `tp_init` явно, так как интерпретатор сделает это сам.453454Реализация `tp_new` вызывает слот [`tp_alloc`](https://python-all.ru/3.7/c-api/typeobj.html#c.PyTypeObject.tp_alloc) для выделения памяти:455456```c457self = (CustomObject *) type->tp_alloc(type, 0);458```459460Поскольку выделение памяти может завершиться неудачей, мы должны проверить результат [`tp_alloc`](https://python-all.ru/3.7/c-api/typeobj.html#c.PyTypeObject.tp_alloc) на `NULL` перед продолжением.461462> **Примечание**463>464> Мы не заполняли слот [`tp_alloc`](https://python-all.ru/3.7/c-api/typeobj.html#c.PyTypeObject.tp_alloc) самостоятельно. Вместо этого [`PyType_Ready()`](https://python-all.ru/3.7/c-api/type.html#c.PyType_Ready) заполняет его для нас, наследуя его от базового класса, которым по умолчанию является [`object`](https://python-all.ru/3.7/library/functions.html#object). Большинство типов используют стратегию выделения по умолчанию.465466> **Примечание**467>468> При создании кооперативного [`tp_new`](https://python-all.ru/3.7/c-api/typeobj.html#c.PyTypeObject.tp_new) (такого, который вызывает [`tp_new`](https://python-all.ru/3.7/c-api/typeobj.html#c.PyTypeObject.tp_new) или [`__new__()`](https://python-all.ru/3.7/reference/datamodel.html#object.__new__) базового типа) нельзя *не* пытаться определить, какой метод вызывать, используя порядок разрешения методов во время выполнения. Всегда статически определяйте тип, который будете вызывать, и вызывайте его [`tp_new`](https://python-all.ru/3.7/c-api/typeobj.html#c.PyTypeObject.tp_new) напрямую или через `type->tp_base->tp_new`. Если этого не делать, подклассы Python вашего типа, которые также наследуются от других классов, определённых в Python, могут работать некорректно. (В частности, вы не сможете создать экземпляры таких подклассов без получения [`TypeError`](https://python-all.ru/3.7/library/exceptions.html#TypeError).)469470Мы также определяем функцию инициализации, которая принимает аргументы для предоставления начальных значений экземпляру:471472```c473static int474Custom_init(CustomObject *self, PyObject *args, PyObject *kwds)475{476    static char *kwlist[] = {"first", "last", "number", NULL};477    PyObject *first = NULL, *last = NULL, *tmp;478479    if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOi", kwlist,480                                     &first, &last,481                                     &self->number))482        return -1;483484    if (first) {485        tmp = self->first;486        Py_INCREF(first);487        self->first = first;488        Py_XDECREF(tmp);489    }490    if (last) {491        tmp = self->last;492        Py_INCREF(last);493        self->last = last;494        Py_XDECREF(tmp);495    }496    return 0;497}498```499500заполняя слот [`tp_init`](https://python-all.ru/3.7/c-api/typeobj.html#c.PyTypeObject.tp_init).501502```c503.tp_init = (initproc) Custom_init,504```505506Слот [`tp_init`](https://python-all.ru/3.7/c-api/typeobj.html#c.PyTypeObject.tp_init) открыт в Python как метод [`__init__()`](https://python-all.ru/3.7/reference/datamodel.html#object.__init__). Он используется для инициализации объекта после его создания. Инициализаторы всегда принимают позиционные и именованные аргументы и должны возвращать либо `0` в случае успеха, либо `-1` в случае ошибки.507508В отличие от обработчика `tp_new`, нет никакой гарантии, что `tp_init` будет вызван (например, модуль [`pickle`](https://python-all.ru/3.7/library/pickle.html#module-pickle) по умолчанию не вызывает [`__init__()`](https://python-all.ru/3.7/reference/datamodel.html#object.__init__) для распакованных экземпляров). Он также может быть вызван несколько раз. Кто угодно может вызвать метод [`__init__()`](https://python-all.ru/3.7/reference/datamodel.html#object.__init__) на наших объектах. По этой причине нужно быть особенно осторожным при присвоении новых значений атрибутов. Может возникнуть соблазн, например, присвоить члену `first` такое значение:509510```c511if (first) {512    Py_XDECREF(self->first);513    Py_INCREF(first);514    self->first = first;515}516```517518But this would be risky. Our type doesn’t restrict the type of the `first` member, so it could be any kind of object. It could have a destructor that causes code to be executed that tries to access the `first` member; or that destructor could release the [Global interpreter Lock](https://python-all.ru/3.7/glossary.html#term-global-interpreter-lock) and let arbitrary code run in other threads that accesses and modifies our object.519520Чтобы быть параноидальными и защитить себя от такой возможности, мы почти всегда переназначаем члены перед уменьшением их счётчиков ссылок. Когда этого делать не нужно?521522- когда мы точно знаем, что счётчик ссылок больше 1;523- when we know that deallocation of the object [1](https://python-all.ru/3.7/extending/newtypes_tutorial.html#id5) will neither release the [GIL](https://python-all.ru/3.7/glossary.html#term-gil) nor cause any calls back into our type’s code;524- когда уменьшается счётчик ссылок в обработчике [`tp_dealloc`](https://python-all.ru/3.7/c-api/typeobj.html#c.PyTypeObject.tp_dealloc) для типа, который не поддерживает циклическую сборку мусора [2](https://python-all.ru/3.7/extending/newtypes_tutorial.html#id6).525526Мы хотим предоставить доступ к переменным экземпляра как к атрибутам. Есть несколько способов сделать это. Самый простой – определить определения членов:527528```c529static PyMemberDef Custom_members[] = {530    {"first", T_OBJECT_EX, offsetof(CustomObject, first), 0,531     "first name"},532    {"last", T_OBJECT_EX, offsetof(CustomObject, last), 0,533     "last name"},534    {"number", T_INT, offsetof(CustomObject, number), 0,535     "custom number"},536    {NULL}  /* Страж */537};538```539540и поместить определения в слот [`tp_members`](https://python-all.ru/3.7/c-api/typeobj.html#c.PyTypeObject.tp_members):541542```c543.tp_members = Custom_members,544```545546Каждое определение члена содержит имя члена, тип, смещение, флаги доступа и строку документации. Подробнее см. раздел [Общее управление атрибутами](https://python-all.ru/3.7/extending/newtypes.html#generic-attribute-management) ниже.547548Недостаток этого подхода в том, что он не даёт способа ограничить типы объектов, которые можно присваивать атрибутам Python. Мы ожидаем, что имя и фамилия будут строками, но можно присвоить любые объекты Python. Кроме того, атрибуты можно удалить, установив указатели C в `NULL`. Даже если мы можем гарантировать, что члены инициализируются значениями, отличными от `NULL`, члены могут быть установлены в `NULL`, если атрибуты удалены.549550Мы определяем один метод `Custom.name()`, который выводит имя объекта как конкатенацию имени и фамилии.551552```c553static PyObject *554Custom_name(CustomObject *self)555{556    if (self->first == NULL) {557        PyErr_SetString(PyExc_AttributeError, "first");558        return NULL;559    }560    if (self->last == NULL) {561        PyErr_SetString(PyExc_AttributeError, "last");562        return NULL;563    }564    return PyUnicode_FromFormat("%S %S", self->first, self->last);565}566```567568Метод реализован как функция на C, которая принимает экземпляр `Custom` (или подкласс `Custom`) в качестве первого аргумента. Методы всегда принимают экземпляр в качестве первого аргумента. Методы часто также принимают позиционные и именованные аргументы, но в данном случае мы не принимаем никаких и не нуждаемся в кортеже позиционных аргументов или словаре именованных аргументов. Этот метод эквивалентен следующему методу Python:569570```python571def name(self):572    return "%s %s" % (self.first, self.last)573```574575Обратите внимание, что нужно проверять возможность того, что наши члены `first` и `last` равны `NULL`. Это потому, что их можно удалить, и в этом случае они устанавливаются в `NULL`. Было бы лучше запретить удаление этих атрибутов и ограничить значения атрибутов строками. Как это сделать, мы увидим в следующем разделе.576577Теперь, когда мы определили метод, нужно создать массив определений методов:578579```c580static PyMethodDef Custom_methods[] = {581    {"name", (PyCFunction) Custom_name, METH_NOARGS,582     "Return the name, combining the first and last name"583    },584    {NULL}  /* Страж */585};586```587588(обратите внимание, что мы использовали флаг [`METH_NOARGS`](https://python-all.ru/3.7/c-api/structures.html#METH_NOARGS), чтобы указать, что метод не ожидает аргументов, кроме *self*)589590и назначить его слоту [`tp_methods`](https://python-all.ru/3.7/c-api/typeobj.html#c.PyTypeObject.tp_methods):591592```c593.tp_methods = Custom_methods,594```595596Наконец, сделаем наш тип пригодным для использования в качестве базового класса для создания подклассов. Мы написали наши методы достаточно осторожно, чтобы они не делали никаких предположений о типе создаваемого или используемого объекта, поэтому всё, что нам нужно сделать, – это добавить [`Py_TPFLAGS_BASETYPE`](https://python-all.ru/3.7/c-api/typeobj.html#Py_TPFLAGS_BASETYPE) в определение флага нашего класса:597598```c599.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,600```601602Мы переименовываем `PyInit_custom()` в `PyInit_custom2()`, обновляем имя модуля в структуре [`PyModuleDef`](https://python-all.ru/3.7/c-api/module.html#c.PyModuleDef) и обновляем полное имя класса в структуре [`PyTypeObject`](https://python-all.ru/3.7/c-api/type.html#c.PyTypeObject).603604Наконец, мы обновляем наш файл `setup.py`, чтобы собрать новый модуль:605606```python607from distutils.core import setup, Extension608setup(name="custom", version="1.0",609      ext_modules=[610         Extension("custom", ["custom.c"]),611         Extension("custom2", ["custom2.c"]),612         ])613```614615## 2.3. Предоставление более точного управления атрибутами данных616617В этом разделе мы предоставим более тонкий контроль над тем, как устанавливаются атрибуты `first` и `last` в примере `Custom`. В предыдущей версии нашего модуля переменные экземпляра `first` и `last` могли быть установлены в нестроковые значения или даже удалены. Мы хотим гарантировать, что эти атрибуты всегда содержат строки.618619```c620#define PY_SSIZE_T_CLEAN621#include <Python.h>622#include "structmember.h"623624typedef struct {625    PyObject_HEAD626    PyObject *first; /* имя */627    PyObject *last;  /* фамилия */628    int number;629} CustomObject;630631static void632Custom_dealloc(CustomObject *self)633{634    Py_XDECREF(self->first);635    Py_XDECREF(self->last);636    Py_TYPE(self)->tp_free((PyObject *) self);637}638639static PyObject *640Custom_new(PyTypeObject *type, PyObject *args, PyObject *kwds)641{642    CustomObject *self;643    self = (CustomObject *) type->tp_alloc(type, 0);644    if (self != NULL) {645        self->first = PyUnicode_FromString("");646        if (self->first == NULL) {647            Py_DECREF(self);648            return NULL;649        }650        self->last = PyUnicode_FromString("");651        if (self->last == NULL) {652            Py_DECREF(self);653            return NULL;654        }655        self->number = 0;656    }657    return (PyObject *) self;658}659660static int661Custom_init(CustomObject *self, PyObject *args, PyObject *kwds)662{663    static char *kwlist[] = {"first", "last", "number", NULL};664    PyObject *first = NULL, *last = NULL, *tmp;665666    if (!PyArg_ParseTupleAndKeywords(args, kwds, "|UUi", kwlist,667                                     &first, &last,668                                     &self->number))669        return -1;670671    if (first) {672        tmp = self->first;673        Py_INCREF(first);674        self->first = first;675        Py_DECREF(tmp);676    }677    if (last) {678        tmp = self->last;679        Py_INCREF(last);680        self->last = last;681        Py_DECREF(tmp);682    }683    return 0;684}685686static PyMemberDef Custom_members[] = {687    {"number", T_INT, offsetof(CustomObject, number), 0,688     "custom number"},689    {NULL}  /* Страж */690};691692static PyObject *693Custom_getfirst(CustomObject *self, void *closure)694{695    Py_INCREF(self->first);696    return self->first;697}698699static int700Custom_setfirst(CustomObject *self, PyObject *value, void *closure)701{702    PyObject *tmp;703    if (value == NULL) {704        PyErr_SetString(PyExc_TypeError, "Cannot delete the first attribute");705        return -1;706    }707    if (!PyUnicode_Check(value)) {708        PyErr_SetString(PyExc_TypeError,709                        "The first attribute value must be a string");710        return -1;711    }712    tmp = self->first;713    Py_INCREF(value);714    self->first = value;715    Py_DECREF(tmp);716    return 0;717}718719static PyObject *720Custom_getlast(CustomObject *self, void *closure)721{722    Py_INCREF(self->last);723    return self->last;724}725726static int727Custom_setlast(CustomObject *self, PyObject *value, void *closure)728{729    PyObject *tmp;730    if (value == NULL) {731        PyErr_SetString(PyExc_TypeError, "Cannot delete the last attribute");732        return -1;733    }734    if (!PyUnicode_Check(value)) {735        PyErr_SetString(PyExc_TypeError,736                        "The last attribute value must be a string");737        return -1;738    }739    tmp = self->last;740    Py_INCREF(value);741    self->last = value;742    Py_DECREF(tmp);743    return 0;744}745746static PyGetSetDef Custom_getsetters[] = {747    {"first", (getter) Custom_getfirst, (setter) Custom_setfirst,748     "first name", NULL},749    {"last", (getter) Custom_getlast, (setter) Custom_setlast,750     "last name", NULL},751    {NULL}  /* Страж */752};753754static PyObject *755Custom_name(CustomObject *self, PyObject *Py_UNUSED(ignored))756{757    return PyUnicode_FromFormat("%S %S", self->first, self->last);758}759760static PyMethodDef Custom_methods[] = {761    {"name", (PyCFunction) Custom_name, METH_NOARGS,762     "Return the name, combining the first and last name"763    },764    {NULL}  /* Страж */765};766767static PyTypeObject CustomType = {768    PyVarObject_HEAD_INIT(NULL, 0)769    .tp_name = "custom3.Custom",770    .tp_doc = "Custom objects",771    .tp_basicsize = sizeof(CustomObject),772    .tp_itemsize = 0,773    .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,774    .tp_new = Custom_new,775    .tp_init = (initproc) Custom_init,776    .tp_dealloc = (destructor) Custom_dealloc,777    .tp_members = Custom_members,778    .tp_methods = Custom_methods,779    .tp_getset = Custom_getsetters,780};781782static PyModuleDef custommodule = {783    PyModuleDef_HEAD_INIT,784    .m_name = "custom3",785    .m_doc = "Example module that creates an extension type.",786    .m_size = -1,787};788789PyMODINIT_FUNC790PyInit_custom3(void)791{792    PyObject *m;793    if (PyType_Ready(&CustomType) < 0)794        return NULL;795796    m = PyModule_Create(&custommodule);797    if (m == NULL)798        return NULL;799800    Py_INCREF(&CustomType);801    if (PyModule_AddObject(m, "Custom", (PyObject *) &CustomType) < 0) {802        Py_DECREF(&CustomType);803        Py_DECREF(m);804        return NULL;805    }806807    return m;808}809```810811Чтобы обеспечить больший контроль над атрибутами `first` и `last`, мы будем использовать пользовательские функции получения и установки. Вот функции для получения и установки атрибута `first`:812813```c814static PyObject *815Custom_getfirst(CustomObject *self, void *closure)816{817    Py_INCREF(self->first);818    return self->first;819}820821static int822Custom_setfirst(CustomObject *self, PyObject *value, void *closure)823{824    PyObject *tmp;825    if (value == NULL) {826        PyErr_SetString(PyExc_TypeError, "Cannot delete the first attribute");827        return -1;828    }829    if (!PyUnicode_Check(value)) {830        PyErr_SetString(PyExc_TypeError,831                        "The first attribute value must be a string");832        return -1;833    }834    tmp = self->first;835    Py_INCREF(value);836    self->first = value;837    Py_DECREF(tmp);838    return 0;839}840```841842Функция получения принимает объект `Custom` и «замыкание», которое является указателем void. В данном случае замыкание игнорируется. (Замыкание поддерживает расширенное использование, при котором данные определения передаются функциям получения и установки. Это может быть использовано, например, для создания единого набора функций получения и установки, которые решают, какой атрибут получить или установить на основе данных в замыкании.)843844Функция установки принимает объект `Custom`, новое значение и замыкание. Новое значение может быть `NULL`, в этом случае атрибут удаляется. В нашей функции установки мы вызываем ошибку, если атрибут удаляется или его новое значение не является строкой.845846Мы создаём массив структур [`PyGetSetDef`](https://python-all.ru/3.7/c-api/structures.html#c.PyGetSetDef):847848```c849static PyGetSetDef Custom_getsetters[] = {850    {"first", (getter) Custom_getfirst, (setter) Custom_setfirst,851     "first name", NULL},852    {"last", (getter) Custom_getlast, (setter) Custom_setlast,853     "last name", NULL},854    {NULL}  /* Страж */855};856```857858и регистрируем его в слоте [`tp_getset`](https://python-all.ru/3.7/c-api/typeobj.html#c.PyTypeObject.tp_getset):859860```c861.tp_getset = Custom_getsetters,862```863864Последний элемент структуры [`PyGetSetDef`](https://python-all.ru/3.7/c-api/structures.html#c.PyGetSetDef) – это «замыкание», упомянутое выше. В данном случае мы не используем замыкание, поэтому просто передаём `NULL`.865866Также удаляем определения членов для этих атрибутов:867868```c869static PyMemberDef Custom_members[] = {870    {"number", T_INT, offsetof(CustomObject, number), 0,871     "custom number"},872    {NULL}  /* Страж */873};874```875876Также нужно обновить обработчик [`tp_init`](https://python-all.ru/3.7/c-api/typeobj.html#c.PyTypeObject.tp_init), чтобы разрешить передачу только строк [3](https://python-all.ru/3.7/extending/newtypes_tutorial.html#id7):877878```c879static int880Custom_init(CustomObject *self, PyObject *args, PyObject *kwds)881{882    static char *kwlist[] = {"first", "last", "number", NULL};883    PyObject *first = NULL, *last = NULL, *tmp;884885    if (!PyArg_ParseTupleAndKeywords(args, kwds, "|UUi", kwlist,886                                     &first, &last,887                                     &self->number))888        return -1;889890    if (first) {891        tmp = self->first;892        Py_INCREF(first);893        self->first = first;894        Py_DECREF(tmp);895    }896    if (last) {897        tmp = self->last;898        Py_INCREF(last);899        self->last = last;900        Py_DECREF(tmp);901    }902    return 0;903}904```905906Благодаря этим изменениям можно гарантировать, что члены `first` и `last` никогда не будут `NULL`, поэтому проверки на значения `NULL` можно убрать почти во всех случаях. Это означает, что большинство вызовов [`Py_XDECREF()`](https://python-all.ru/3.7/c-api/refcounting.html#c.Py_XDECREF) можно преобразовать в вызовы [`Py_DECREF()`](https://python-all.ru/3.7/c-api/refcounting.html#c.Py_DECREF). Единственное место, где эти вызовы нельзя изменить, – реализация `tp_dealloc`, где возможна ситуация, что инициализация этих членов завершилась неудачей в `tp_new`.907908Также переименовываем функцию инициализации модуля и имя модуля в функции инициализации, как делали раньше, и добавляем дополнительное определение в файл `setup.py`.909910## 2.4. Поддержка циклической сборки мусора911912Python имеет [циклический сборщик мусора (GC)](https://python-all.ru/3.7/glossary.html#term-garbage-collection), который может определять ненужные объекты даже при ненулевом счётчике ссылок. Это возможно, когда объекты образуют циклы. Например, рассмотрим:913914```pycon915>>> l = []916>>> l.append(l)917>>> del l918```919920В этом примере создаётся список, содержащий сам себя. При его удалении у него всё ещё остаётся ссылка на самого себя. Счётчик ссылок не падает до нуля. К счастью, циклический сборщик мусора Python в конечном итоге определит, что список является мусором, и освободит его.921922Во второй версии примера `Custom` мы разрешили хранить объекты любого типа в атрибутах `first` или `last` [4](https://python-all.ru/3.7/extending/newtypes_tutorial.html#id8). Кроме того, во второй и третьей версиях мы разрешили наследование от `Custom`, а подклассы могут добавлять произвольные атрибуты. По любой из этих двух причин объекты `Custom` могут участвовать в циклах:923924```pycon925>>> import custom3926>>> class Derived(custom3.Custom): pass927...928>>> n = Derived()929>>> n.some_attribute = n930```931932Чтобы экземпляр `Custom`, участвующий в цикле ссылок, был корректно обнаружен и собран циклическим GC, наш тип `Custom` должен заполнить два дополнительных слота и включить флаг, активирующий эти слоты:933934```c935#define PY_SSIZE_T_CLEAN936#include <Python.h>937#include "structmember.h"938939typedef struct {940    PyObject_HEAD941    PyObject *first; /* имя */942    PyObject *last;  /* фамилия */943    int number;944} CustomObject;945946static int947Custom_traverse(CustomObject *self, visitproc visit, void *arg)948{949    Py_VISIT(self->first);950    Py_VISIT(self->last);951    return 0;952}953954static int955Custom_clear(CustomObject *self)956{957    Py_CLEAR(self->first);958    Py_CLEAR(self->last);959    return 0;960}961962static void963Custom_dealloc(CustomObject *self)964{965    PyObject_GC_UnTrack(self);966    Custom_clear(self);967    Py_TYPE(self)->tp_free((PyObject *) self);968}969970static PyObject *971Custom_new(PyTypeObject *type, PyObject *args, PyObject *kwds)972{973    CustomObject *self;974    self = (CustomObject *) type->tp_alloc(type, 0);975    if (self != NULL) {976        self->first = PyUnicode_FromString("");977        if (self->first == NULL) {978            Py_DECREF(self);979            return NULL;980        }981        self->last = PyUnicode_FromString("");982        if (self->last == NULL) {983            Py_DECREF(self);984            return NULL;985        }986        self->number = 0;987    }988    return (PyObject *) self;989}990991static int992Custom_init(CustomObject *self, PyObject *args, PyObject *kwds)993{994    static char *kwlist[] = {"first", "last", "number", NULL};995    PyObject *first = NULL, *last = NULL, *tmp;996997    if (!PyArg_ParseTupleAndKeywords(args, kwds, "|UUi", kwlist,998                                     &first, &last,999                                     &self->number))1000        return -1;10011002    if (first) {1003        tmp = self->first;1004        Py_INCREF(first);1005        self->first = first;1006        Py_DECREF(tmp);1007    }1008    if (last) {1009        tmp = self->last;1010        Py_INCREF(last);1011        self->last = last;1012        Py_DECREF(tmp);1013    }1014    return 0;1015}10161017static PyMemberDef Custom_members[] = {1018    {"number", T_INT, offsetof(CustomObject, number), 0,1019     "custom number"},1020    {NULL}  /* Страж */1021};10221023static PyObject *1024Custom_getfirst(CustomObject *self, void *closure)1025{1026    Py_INCREF(self->first);1027    return self->first;1028}10291030static int1031Custom_setfirst(CustomObject *self, PyObject *value, void *closure)1032{1033    if (value == NULL) {1034        PyErr_SetString(PyExc_TypeError, "Cannot delete the first attribute");1035        return -1;1036    }1037    if (!PyUnicode_Check(value)) {1038        PyErr_SetString(PyExc_TypeError,1039                        "The first attribute value must be a string");1040        return -1;1041    }1042    Py_INCREF(value);1043    Py_CLEAR(self->first);1044    self->first = value;1045    return 0;1046}10471048static PyObject *1049Custom_getlast(CustomObject *self, void *closure)1050{1051    Py_INCREF(self->last);1052    return self->last;1053}10541055static int1056Custom_setlast(CustomObject *self, PyObject *value, void *closure)1057{1058    if (value == NULL) {1059        PyErr_SetString(PyExc_TypeError, "Cannot delete the last attribute");1060        return -1;1061    }1062    if (!PyUnicode_Check(value)) {1063        PyErr_SetString(PyExc_TypeError,1064                        "The last attribute value must be a string");1065        return -1;1066    }1067    Py_INCREF(value);1068    Py_CLEAR(self->last);1069    self->last = value;1070    return 0;1071}10721073static PyGetSetDef Custom_getsetters[] = {1074    {"first", (getter) Custom_getfirst, (setter) Custom_setfirst,1075     "first name", NULL},1076    {"last", (getter) Custom_getlast, (setter) Custom_setlast,1077     "last name", NULL},1078    {NULL}  /* Страж */1079};10801081static PyObject *1082Custom_name(CustomObject *self, PyObject *Py_UNUSED(ignored))1083{1084    return PyUnicode_FromFormat("%S %S", self->first, self->last);1085}10861087static PyMethodDef Custom_methods[] = {1088    {"name", (PyCFunction) Custom_name, METH_NOARGS,1089     "Return the name, combining the first and last name"1090    },1091    {NULL}  /* Страж */1092};10931094static PyTypeObject CustomType = {1095    PyVarObject_HEAD_INIT(NULL, 0)1096    .tp_name = "custom4.Custom",1097    .tp_doc = "Custom objects",1098    .tp_basicsize = sizeof(CustomObject),1099    .tp_itemsize = 0,1100    .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,1101    .tp_new = Custom_new,1102    .tp_init = (initproc) Custom_init,1103    .tp_dealloc = (destructor) Custom_dealloc,1104    .tp_traverse = (traverseproc) Custom_traverse,1105    .tp_clear = (inquiry) Custom_clear,1106    .tp_members = Custom_members,1107    .tp_methods = Custom_methods,1108    .tp_getset = Custom_getsetters,1109};11101111static PyModuleDef custommodule = {1112    PyModuleDef_HEAD_INIT,1113    .m_name = "custom4",1114    .m_doc = "Example module that creates an extension type.",1115    .m_size = -1,1116};11171118PyMODINIT_FUNC1119PyInit_custom4(void)1120{1121    PyObject *m;1122    if (PyType_Ready(&CustomType) < 0)1123        return NULL;11241125    m = PyModule_Create(&custommodule);1126    if (m == NULL)1127        return NULL;11281129    Py_INCREF(&CustomType);1130    if (PyModule_AddObject(m, "Custom", (PyObject *) &CustomType) < 0) {1131        Py_DECREF(&CustomType);1132        Py_DECREF(m);1133        return NULL;1134    }11351136    return m;1137}1138```11391140Во-первых, метод обхода (traversal) сообщает циклическому GC о подобъектах, которые могут участвовать в циклах:11411142```c1143static int1144Custom_traverse(CustomObject *self, visitproc visit, void *arg)1145{1146    int vret;1147    if (self->first) {1148        vret = visit(self->first, arg);1149        if (vret != 0)1150            return vret;1151    }1152    if (self->last) {1153        vret = visit(self->last, arg);1154        if (vret != 0)1155            return vret;1156    }1157    return 0;1158}1159```11601161Для каждого подобъекта, который может участвовать в циклах, нужно вызвать функцию `visit()`, передаваемую методу обхода. Функция `visit()` принимает в качестве аргументов подобъект и дополнительный аргумент *arg*, переданный методу обхода. Она возвращает целое значение, которое должно быть возвращено, если оно ненулевое.11621163Python предоставляет макрос [`Py_VISIT()`](https://python-all.ru/3.7/c-api/gcsupport.html#c.Py_VISIT), автоматизирующий вызов функций обхода. С помощью [`Py_VISIT()`](https://python-all.ru/3.7/c-api/gcsupport.html#c.Py_VISIT) можно минимизировать количество шаблонного кода в `Custom_traverse`:11641165```c1166static int1167Custom_traverse(CustomObject *self, visitproc visit, void *arg)1168{1169    Py_VISIT(self->first);1170    Py_VISIT(self->last);1171    return 0;1172}1173```11741175> **Примечание**1176>1177> Реализация [`tp_traverse`](https://python-all.ru/3.7/c-api/typeobj.html#c.PyTypeObject.tp_traverse) должна именовать свои аргументы именно как *visit* и *arg*, чтобы использовать [`Py_VISIT()`](https://python-all.ru/3.7/c-api/gcsupport.html#c.Py_VISIT).11781179Во-вторых, нужно предоставить метод для очистки любых подобъектов, которые могут участвовать в циклах:11801181```c1182static int1183Custom_clear(CustomObject *self)1184{1185    Py_CLEAR(self->first);1186    Py_CLEAR(self->last);1187    return 0;1188}1189```11901191Обратите внимание на использование макроса [`Py_CLEAR()`](https://python-all.ru/3.7/c-api/refcounting.html#c.Py_CLEAR). Это рекомендуемый и безопасный способ очистки атрибутов данных произвольных типов с одновременным уменьшением их счётчиков ссылок. Если вместо этого вызвать [`Py_XDECREF()`](https://python-all.ru/3.7/c-api/refcounting.html#c.Py_XDECREF) для атрибута перед установкой его в `NULL`, существует вероятность, что деструктор атрибута повторно вызовет код, читающий этот атрибут (*особенно* при наличии циклической ссылки).11921193> **Примечание**1194>1195> Можно эмулировать [`Py_CLEAR()`](https://python-all.ru/3.7/c-api/refcounting.html#c.Py_CLEAR) следующим образом:1196>1197> ```c1198> PyObject *tmp;1199> tmp = self->first;1200> self->first = NULL;1201> Py_XDECREF(tmp);1202> ```1203>1204> Тем не менее, гораздо проще и менее чревато ошибками всегда использовать [`Py_CLEAR()`](https://python-all.ru/3.7/c-api/refcounting.html#c.Py_CLEAR) при удалении атрибута. Не пытайтесь микрооптимизировать в ущерб надёжности!12051206Деаллокатор `Custom_dealloc` может вызывать произвольный код при очистке атрибутов. Это означает, что циклический GC может быть запущен внутри функции. Поскольку GC предполагает, что счётчик ссылок не равен нулю, нужно отменить отслеживание объекта GC с помощью вызова [`PyObject_GC_UnTrack()`](https://python-all.ru/3.7/c-api/gcsupport.html#c.PyObject_GC_UnTrack) перед очисткой членов. Вот наша переработанная версия деаллокатора с использованием [`PyObject_GC_UnTrack()`](https://python-all.ru/3.7/c-api/gcsupport.html#c.PyObject_GC_UnTrack) и `Custom_clear`:12071208```c1209static void1210Custom_dealloc(CustomObject *self)1211{1212    PyObject_GC_UnTrack(self);1213    Custom_clear(self);1214    Py_TYPE(self)->tp_free((PyObject *) self);1215}1216```12171218Наконец, добавляем флаг [`Py_TPFLAGS_HAVE_GC`](https://python-all.ru/3.7/c-api/typeobj.html#Py_TPFLAGS_HAVE_GC) в флаги класса:12191220```c1221.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,1222```12231224Вот, в общем-то, и всё. Если бы мы написали собственные обработчики [`tp_alloc`](https://python-all.ru/3.7/c-api/typeobj.html#c.PyTypeObject.tp_alloc) или [`tp_free`](https://python-all.ru/3.7/c-api/typeobj.html#c.PyTypeObject.tp_free), их пришлось бы доработать для поддержки циклической сборки мусора. Большинство расширений используют автоматически предоставляемые версии.12251226## 2.5. Наследование от других типов12271228Можно создавать новые типы расширений, производные от существующих типов. Проще всего наследовать от встроенных типов, поскольку расширение может легко использовать нужный ему [`PyTypeObject`](https://python-all.ru/3.7/c-api/type.html#c.PyTypeObject). Совместное использование структур [`PyTypeObject`](https://python-all.ru/3.7/c-api/type.html#c.PyTypeObject) между модулями расширений может быть затруднительным.12291230В этом примере мы создадим тип `SubList`, наследующий от встроенного типа [`list`](https://python-all.ru/3.7/library/stdtypes.html#list). Новый тип будет полностью совместим с обычными списками, но будет иметь дополнительный метод `increment()`, увеличивающий внутренний счётчик:12311232```pycon1233>>> import sublist1234>>> s = sublist.SubList(range(3))1235>>> s.extend(s)1236>>> print(len(s))123761238>>> print(s.increment())123911240>>> print(s.increment())124121242```12431244```c1245#define PY_SSIZE_T_CLEAN1246#include <Python.h>12471248typedef struct {1249    PyListObject list;1250    int state;1251} SubListObject;12521253static PyObject *1254SubList_increment(SubListObject *self, PyObject *unused)1255{1256    self->state++;1257    return PyLong_FromLong(self->state);1258}12591260static PyMethodDef SubList_methods[] = {1261    {"increment", (PyCFunction) SubList_increment, METH_NOARGS,1262     PyDoc_STR("increment state counter")},1263    {NULL},1264};12651266static int1267SubList_init(SubListObject *self, PyObject *args, PyObject *kwds)1268{1269    if (PyList_Type.tp_init((PyObject *) self, args, kwds) < 0)1270        return -1;1271    self->state = 0;1272    return 0;1273}12741275static PyTypeObject SubListType = {1276    PyVarObject_HEAD_INIT(NULL, 0)1277    .tp_name = "sublist.SubList",1278    .tp_doc = "SubList objects",1279    .tp_basicsize = sizeof(SubListObject),1280    .tp_itemsize = 0,1281    .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,1282    .tp_init = (initproc) SubList_init,1283    .tp_methods = SubList_methods,1284};12851286static PyModuleDef sublistmodule = {1287    PyModuleDef_HEAD_INIT,1288    .m_name = "sublist",1289    .m_doc = "Example module that creates an extension type.",1290    .m_size = -1,1291};12921293PyMODINIT_FUNC1294PyInit_sublist(void)1295{1296    PyObject *m;1297    SubListType.tp_base = &PyList_Type;1298    if (PyType_Ready(&SubListType) < 0)1299        return NULL;13001301    m = PyModule_Create(&sublistmodule);1302    if (m == NULL)1303        return NULL;13041305    Py_INCREF(&SubListType);1306    if (PyModule_AddObject(m, "SubList", (PyObject *) &SubListType) < 0) {1307        Py_DECREF(&SubListType);1308        Py_DECREF(m);1309        return NULL;1310    }13111312    return m;1313}1314```13151316Как видите, исходный код очень похож на примеры `Custom` из предыдущих разделов. Разберём основные различия между ними.13171318```c1319typedef struct {1320    PyListObject list;1321    int state;1322} SubListObject;1323```13241325Основное отличие для объектов производных типов в том, что структура объекта базового типа должна быть первым значением. Базовый тип уже включает [`PyObject_HEAD()`](https://python-all.ru/3.7/c-api/structures.html#c.PyObject_HEAD) в начале своей структуры.13261327Когда объект Python является экземпляром `SubList`, его указатель `PyObject *` можно безопасно приводить как к `PyListObject *`, так и к `SubListObject *`:13281329```c1330static int1331SubList_init(SubListObject *self, PyObject *args, PyObject *kwds)1332{1333    if (PyList_Type.tp_init((PyObject *) self, args, kwds) < 0)1334        return -1;1335    self->state = 0;1336    return 0;1337}1338```13391340Выше показано, как вызывать метод `__init__` базового типа.13411342Этот шаблон важен при написании типа с пользовательскими членами [`tp_new`](https://python-all.ru/3.7/c-api/typeobj.html#c.PyTypeObject.tp_new) и [`tp_dealloc`](https://python-all.ru/3.7/c-api/typeobj.html#c.PyTypeObject.tp_dealloc). Обработчик [`tp_new`](https://python-all.ru/3.7/c-api/typeobj.html#c.PyTypeObject.tp_new) не должен фактически выделять память для объекта с помощью своего [`tp_alloc`](https://python-all.ru/3.7/c-api/typeobj.html#c.PyTypeObject.tp_alloc), а должен позволить базовому классу обработать это, вызвав его собственный [`tp_new`](https://python-all.ru/3.7/c-api/typeobj.html#c.PyTypeObject.tp_new).13431344Структура [`PyTypeObject`](https://python-all.ru/3.7/c-api/type.html#c.PyTypeObject) поддерживает [`tp_base`](https://python-all.ru/3.7/c-api/typeobj.html#c.PyTypeObject.tp_base) указывающее конкретный базовый класс типа. Из-за проблем с кроссплатформенными компиляторами нельзя заполнить это поле напрямую ссылкой на [`PyList_Type`](https://python-all.ru/3.7/c-api/list.html#c.PyList_Type); это следует сделать позже, в функции инициализации модуля:13451346```c1347PyMODINIT_FUNC1348PyInit_sublist(void)1349{1350    PyObject* m;1351    SubListType.tp_base = &PyList_Type;1352    if (PyType_Ready(&SubListType) < 0)1353        return NULL;13541355    m = PyModule_Create(&sublistmodule);1356    if (m == NULL)1357        return NULL;13581359    Py_INCREF(&SubListType);1360    if (PyModule_AddObject(m, "SubList", (PyObject *) &SubListType) < 0) {1361        Py_DECREF(&SubListType);1362        Py_DECREF(m);1363        return NULL;1364    }13651366    return m;1367}1368```13691370Перед вызовом [`PyType_Ready()`](https://python-all.ru/3.7/c-api/type.html#c.PyType_Ready) структура типа должна иметь заполненный слот [`tp_base`](https://python-all.ru/3.7/c-api/typeobj.html#c.PyTypeObject.tp_base). При наследовании от существующего типа необязательно заполнять слот [`tp_alloc`](https://python-all.ru/3.7/c-api/typeobj.html#c.PyTypeObject.tp_alloc) значением [`PyType_GenericNew()`](https://python-all.ru/3.7/c-api/type.html#c.PyType_GenericNew) – функция выделения памяти из базового типа будет унаследована.13711372После этого вызов [`PyType_Ready()`](https://python-all.ru/3.7/c-api/type.html#c.PyType_Ready) и добавление объекта типа в модуль выполняется так же, как в базовых примерах `Custom`.13731374Сноски13751376**[1](https://python-all.ru/3.7/extending/newtypes_tutorial.html#id1)**13771378Это верно, когда известно, что объект относится к базовому типу, например, строке или числу с плавающей запятой.13791380**[2](https://python-all.ru/3.7/extending/newtypes_tutorial.html#id2)**13811382На это полагались в обработчике [`tp_dealloc`](https://python-all.ru/3.7/c-api/typeobj.html#c.PyTypeObject.tp_dealloc) в данном примере, поскольку тип не поддерживает сборку мусора.13831384**[3](https://python-all.ru/3.7/extending/newtypes_tutorial.html#id3)**13851386Теперь известно, что первый и последний элементы являются строками, поэтому можно было бы менее осторожно уменьшать их счётчики ссылок. Однако допускаются экземпляры подклассов строк. Даже если освобождение обычных строк не вызовет обратного вызова в наши объекты, нельзя гарантировать, что освобождение экземпляра подкласса строки не вызовет такого обратного вызова.13871388**[4](https://python-all.ru/3.7/extending/newtypes_tutorial.html#id4)**13891390Кроме того, даже если атрибуты ограничены экземплярами строк, пользователь может передать произвольные подклассы [`str`](https://python-all.ru/3.7/library/stdtypes.html#str) и тем самым всё равно создать циклические ссылки.1391