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

newtypes_tutorial.md

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

1> **Источник:** https://python-all.ru/3.6/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.6/library/stdtypes.html#str) и [`list`](https://python-all.ru/3.6/library/stdtypes.html#list). Код для всех типов расширений следует определённому шаблону, но есть некоторые детали, которые необходимо понять, прежде чем приступить к работе. Этот документ является вводным руководством по данной теме.1011## 2.1. Основы1213Среда выполнения [CPython](https://python-all.ru/3.6/glossary.html#term-cpython) рассматривает все объекты Python как переменные типа [`PyObject*`](https://python-all.ru/3.6/c-api/structures.html#c.PyObject), который служит «базовым типом» для всех объектов Python. Сама структура [`PyObject`](https://python-all.ru/3.6/c-api/structures.html#c.PyObject) содержит только [счётчик ссылок](https://python-all.ru/3.6/glossary.html#term-reference-count) объекта и указатель на «объект типа» объекта. В этом и заключается суть: объект типа определяет, какие (C) функции вызываются интерпретатором, когда, например, выполняется поиск атрибута объекта, вызов метода или его умножение на другой объект. Эти C-функции называются «методами типа».1415Итак, чтобы определить новый тип расширения, необходимо создать новый объект типа.1617Такие вещи можно объяснить только на примере, поэтому вот минимальный, но полный модуль, определяющий новый тип с именем `Custom` внутри C-модуля расширения `custom`:1819> **Примечание**20>21> То, что мы показываем здесь, – это традиционный способ определения *статических* типов расширений. Он должен быть достаточным для большинства случаев. C API также позволяет определять типы расширений, размещаемые в куче, с помощью функции [`PyType_FromSpec()`](https://python-all.ru/3.6/c-api/type.html#c.PyType_FromSpec), которая не рассматривается в этом руководстве.2223```c24#include <Python.h>2526typedef struct {27    PyObject_HEAD28    /* Сюда помещаются поля, специфичные для типа. */29} CustomObject;3031static PyTypeObject CustomType = {32    PyVarObject_HEAD_INIT(NULL, 0)33    .tp_name = "custom.Custom",34    .tp_doc = "Custom objects",35    .tp_basicsize = sizeof(CustomObject),36    .tp_itemsize = 0,37    .tp_flags = Py_TPFLAGS_DEFAULT,38    .tp_new = PyType_GenericNew,39};4041static PyModuleDef custommodule = {42    PyModuleDef_HEAD_INIT,43    .m_name = "custom",44    .m_doc = "Example module that creates an extension type.",45    .m_size = -1,46};4748PyMODINIT_FUNC49PyInit_custom(void)50{51    PyObject *m;52    if (PyType_Ready(&CustomType) < 0)53        return NULL;5455    m = PyModule_Create(&custommodule);56    if (m == NULL)57        return NULL;5859    Py_INCREF(&CustomType);60    PyModule_AddObject(m, "Custom", (PyObject *) &CustomType);61    return m;62}63```6465Это довольно много для одномоментного восприятия, но, надеюсь, некоторые части покажутся знакомыми из предыдущей главы. Этот файл определяет три вещи:66671. Что содержит `Custom` **объект**: это структура `CustomObject`, которая выделяется один раз для каждого экземпляра `Custom`.682. Как ведёт себя `Custom` **тип**: это структура `CustomType`, которая определяет набор флагов и указателей на функции, которые интерпретатор проверяет при запросе определённых операций.693. Как инициализировать модуль `custom`: это `PyInit_custom` функция и связанная с ней структура `custommodule`.7071Первая часть:7273```c74typedef struct {75    PyObject_HEAD76} CustomObject;77```7879Вот что будет содержать пользовательский объект. `PyObject_HEAD` является обязательным в начале каждой структуры объекта и определяет поле `ob_base` типа [`PyObject`](https://python-all.ru/3.6/c-api/structures.html#c.PyObject), содержащее указатель на объект типа и счётчик ссылок (доступ к ним можно получить с помощью макросов [`Py_REFCNT`](https://python-all.ru/3.6/c-api/structures.html#c.Py_REFCNT) и [`Py_TYPE`](https://python-all.ru/3.6/c-api/structures.html#c.Py_TYPE) соответственно). Причина использования макроса – абстрагирование от компоновки и возможность добавления дополнительных полей в отладочных сборках.8081> **Примечание**82>83> В приведённом выше коде нет точки с запятой после макроса [`PyObject_HEAD`](https://python-all.ru/3.6/c-api/structures.html#c.PyObject_HEAD). Будьте осторожны, чтобы случайно её не добавить: некоторые компиляторы выдадут ошибку.8485Конечно, объекты обычно хранят дополнительные данные помимо стандартного шаблона `PyObject_HEAD`; например, вот определение для стандартных чисел с плавающей запятой Python:8687```c88typedef struct {89    PyObject_HEAD90    double ob_fval;91} PyFloatObject;92```9394Вторая часть – это определение объекта типа.9596```c97static PyTypeObject CustomType = {98    PyVarObject_HEAD_INIT(NULL, 0)99    .tp_name = "custom.Custom",100    .tp_doc = "Custom objects",101    .tp_basicsize = sizeof(CustomObject),102    .tp_itemsize = 0,103    .tp_new = PyType_GenericNew,104};105```106107> **Примечание**108>109> Рекомендуется использовать именованные инициализаторы в стиле C99, как указано выше, чтобы не перечислять все поля [`PyTypeObject`](https://python-all.ru/3.6/c-api/type.html#c.PyTypeObject), которые не важны, а также не беспокоиться о порядке объявления полей.110111Фактическое определение [`PyTypeObject`](https://python-all.ru/3.6/c-api/type.html#c.PyTypeObject) в `object.h` содержит гораздо больше [полей](https://python-all.ru/3.6/c-api/typeobj.html#type-structs), чем приведённое выше определение. Оставшиеся поля будут заполнены нулями компилятором C, и обычно принято не указывать их явно, если они не нужны.112113Мы разберём его, поле за полем:114115```c116PyVarObject_HEAD_INIT(NULL, 0)117```118119Эта строка является обязательным шаблоном для инициализации поля `ob_base`, упомянутого выше.120121```c122.tp_name = "custom.Custom",123```124125Имя нашего типа. Оно будет отображаться в стандартном текстовом представлении наших объектов и в некоторых сообщениях об ошибках, например:126127```pycon128>>> "" + custom.Custom()129Traceback (most recent call last):130  File "<stdin>", line 1, in <module>131TypeError: can only concatenate str (not "custom.Custom") to str132```133134Обратите внимание, что имя является составным (с точками) и включает как имя модуля, так и имя типа внутри модуля. В данном случае модуль – `custom`, а тип – `Custom`, поэтому мы устанавливаем имя типа `custom.Custom`. Использование реального составного пути импорта важно для совместимости вашего типа с модулями [`pydoc`](https://python-all.ru/3.6/library/pydoc.html#module-pydoc) и [`pickle`](https://python-all.ru/3.6/library/pickle.html#module-pickle).135136```c137.tp_basicsize = sizeof(CustomObject),138.tp_itemsize = 0,139```140141Это нужно, чтобы Python знал, сколько памяти выделять при создании новых экземпляров `Custom`. [`tp_itemsize`](https://python-all.ru/3.6/c-api/typeobj.html#c.PyTypeObject.tp_itemsize) используется только для объектов переменного размера, в остальных случаях должно быть равно нулю.142143> **Примечание**144>145> Если вы хотите, чтобы ваш тип можно было наследовать из Python, и ваш тип имеет тот же [`tp_basicsize`](https://python-all.ru/3.6/c-api/typeobj.html#c.PyTypeObject.tp_basicsize), что и его базовый тип, у вас могут возникнуть проблемы с множественным наследованием. Подкласс вашего типа на Python должен будет указать ваш тип первым в своём [`__bases__`](https://python-all.ru/3.6/library/stdtypes.html#class.__bases__), иначе он не сможет вызвать метод [`__new__()`](https://python-all.ru/3.6/reference/datamodel.html#object.__new__) вашего типа без ошибки. Вы можете избежать этой проблемы, обеспечив большее значение для [`tp_basicsize`](https://python-all.ru/3.6/c-api/typeobj.html#c.PyTypeObject.tp_basicsize) вашего типа по сравнению с его базовым типом. В большинстве случаев это будет верно, потому что либо ваш базовый тип будет [`object`](https://python-all.ru/3.6/library/functions.html#object), либо вы будете добавлять члены данных в базовый тип, тем самым увеличивая его размер.146147Мы устанавливаем флаги класса в [`Py_TPFLAGS_DEFAULT`](https://python-all.ru/3.6/c-api/typeobj.html#Py_TPFLAGS_DEFAULT).148149```c150.tp_flags = Py_TPFLAGS_DEFAULT,151```152153Все типы должны включать эту константу в свои флаги. Она включает все члены, определённые до Python 3.3 как минимум. Если вам нужны дополнительные члены, вам потребуется выполнить OR с соответствующими флагами.154155Мы предоставляем строку документации для типа в [`tp_doc`](https://python-all.ru/3.6/c-api/typeobj.html#c.PyTypeObject.tp_doc).156157```c158.tp_doc = "Custom objects",159```160161Чтобы разрешить создание объектов, мы должны предоставить обработчик [`tp_new`](https://python-all.ru/3.6/c-api/typeobj.html#c.PyTypeObject.tp_new). Это эквивалент метода Python [`__new__()`](https://python-all.ru/3.6/reference/datamodel.html#object.__new__), но должен быть указан явно. В данном случае мы можем просто использовать реализацию по умолчанию, предоставляемую API-функцией [`PyType_GenericNew()`](https://python-all.ru/3.6/c-api/type.html#c.PyType_GenericNew).162163```c164.tp_new = PyType_GenericNew,165```166167Всё остальное в файле должно быть знакомым, за исключением некоторого кода в `PyInit_custom()`:168169```c170if (PyType_Ready(&CustomType) < 0)171    return;172```173174Это инициализирует тип `Custom`, заполняя ряд членов соответствующими значениями по умолчанию, включая `ob_type`, который мы изначально установили в *NULL*.175176```c177PyModule_AddObject(m, "Custom", (PyObject *) &CustomType);178```179180Это добавляет тип в словарь модуля. Это позволяет нам создавать экземпляры `Custom` вызовом класса `Custom`:181182```pycon183>>> import custom184>>> mycustom = custom.Custom()185```186187Вот и всё! Осталось только собрать его; поместите приведённый выше код в файл с именем `custom.c` и:188189```python190from distutils.core import setup, Extension191setup(name="custom", version="1.0",192      ext_modules=[Extension("custom", ["custom.c"])])193```194195в файл с именем `setup.py`; затем введите196197```console198$ python setup.py build199```200201в командной оболочке должен создать файл `custom.so` в подкаталоге; перейдите в этот каталог и запустите Python – вы сможете выполнить `import custom` и поработать с объектами Custom.202203Было не так сложно, правда?204205Конечно, текущий тип Custom довольно неинтересен. У него нет данных, и он ничего не делает. Его даже нельзя унаследовать.206207> **Примечание**208>209> Хотя в этой документации демонстрируется стандартный модуль [`distutils`](https://python-all.ru/3.6/library/distutils.html#module-distutils) для создания расширений на C, в реальных проектах рекомендуется использовать более новую и лучше поддерживаемую библиотеку `setuptools`. Документация о том, как это сделать, выходит за рамки данного документа и находится в [Руководстве пользователя по упаковке Python](https://python-all.ru/3.6/extending/newtypes_tutorial.html).210211## 2.2. Добавление данных и методов к базовому примеру212213Давайте расширим базовый пример, добавив некоторые данные и методы. Также сделаем тип пригодным для использования в качестве базового класса. Мы создадим новый модуль `custom2`, который добавляет эти возможности:214215```c216#include <Python.h>217#include "structmember.h"218219typedef struct {220    PyObject_HEAD221    PyObject *first; /* имя */222    PyObject *last;  /* фамилия */223    int number;224} CustomObject;225226static void227Custom_dealloc(CustomObject *self)228{229    Py_XDECREF(self->first);230    Py_XDECREF(self->last);231    Py_TYPE(self)->tp_free((PyObject *) self);232}233234static PyObject *235Custom_new(PyTypeObject *type, PyObject *args, PyObject *kwds)236{237    CustomObject *self;238    self = (CustomObject *) type->tp_alloc(type, 0);239    if (self != NULL) {240        self->first = PyUnicode_FromString("");241        if (self->first == NULL) {242            Py_DECREF(self);243            return NULL;244        }245        self->last = PyUnicode_FromString("");246        if (self->last == NULL) {247            Py_DECREF(self);248            return NULL;249        }250        self->number = 0;251    }252    return (PyObject *) self;253}254255static int256Custom_init(CustomObject *self, PyObject *args, PyObject *kwds)257{258    static char *kwlist[] = {"first", "last", "number", NULL};259    PyObject *first = NULL, *last = NULL, *tmp;260261    if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOi", kwlist,262                                     &first, &last,263                                     &self->number))264        return -1;265266    if (first) {267        tmp = self->first;268        Py_INCREF(first);269        self->first = first;270        Py_XDECREF(tmp);271    }272    if (last) {273        tmp = self->last;274        Py_INCREF(last);275        self->last = last;276        Py_XDECREF(tmp);277    }278    return 0;279}280281static PyMemberDef Custom_members[] = {282    {"first", T_OBJECT_EX, offsetof(CustomObject, first), 0,283     "first name"},284    {"last", T_OBJECT_EX, offsetof(CustomObject, last), 0,285     "last name"},286    {"number", T_INT, offsetof(CustomObject, number), 0,287     "custom number"},288    {NULL}  /* Страж */289};290291static PyObject *292Custom_name(CustomObject *self, PyObject *Py_UNUSED(ignored))293{294    if (self->first == NULL) {295        PyErr_SetString(PyExc_AttributeError, "first");296        return NULL;297    }298    if (self->last == NULL) {299        PyErr_SetString(PyExc_AttributeError, "last");300        return NULL;301    }302    return PyUnicode_FromFormat("%S %S", self->first, self->last);303}304305static PyMethodDef Custom_methods[] = {306    {"name", (PyCFunction) Custom_name, METH_NOARGS,307     "Return the name, combining the first and last name"308    },309    {NULL}  /* Страж */310};311312static PyTypeObject CustomType = {313    PyVarObject_HEAD_INIT(NULL, 0)314    .tp_name = "custom2.Custom",315    .tp_doc = "Custom objects",316    .tp_basicsize = sizeof(CustomObject),317    .tp_itemsize = 0,318    .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,319    .tp_new = Custom_new,320    .tp_init = (initproc) Custom_init,321    .tp_dealloc = (destructor) Custom_dealloc,322    .tp_members = Custom_members,323    .tp_methods = Custom_methods,324};325326static PyModuleDef custommodule = {327    PyModuleDef_HEAD_INIT,328    .m_name = "custom2",329    .m_doc = "Example module that creates an extension type.",330    .m_size = -1,331};332333PyMODINIT_FUNC334PyInit_custom2(void)335{336    PyObject *m;337    if (PyType_Ready(&CustomType) < 0)338        return NULL;339340    m = PyModule_Create(&custommodule);341    if (m == NULL)342        return NULL;343344    Py_INCREF(&CustomType);345    PyModule_AddObject(m, "Custom", (PyObject *) &CustomType);346    return m;347}348```349350Эта версия модуля содержит ряд изменений.351352Мы добавили дополнительный include:353354```c355#include <structmember.h>356```357358Этот include предоставляет объявления, которые мы используем для обработки атрибутов, как описано немного позже.359360Тип `Custom` теперь имеет три атрибута данных в своей C-структуре: *first*, *last* и *number*. Переменные *first* и *last* являются строками Python, содержащими имя и фамилию. Атрибут *number* – это целое число C.361362Структура объекта обновляется соответствующим образом:363364```c365typedef struct {366    PyObject_HEAD367    PyObject *first; /* имя */368    PyObject *last;  /* фамилия */369    int number;370} CustomObject;371```372373Поскольку теперь у нас есть данные для управления, мы должны быть более внимательны к выделению и освобождению памяти объектов. Как минимум, нам нужен метод освобождения:374375```c376static void377Custom_dealloc(CustomObject *self)378{379    Py_XDECREF(self->first);380    Py_XDECREF(self->last);381    Py_TYPE(self)->tp_free((PyObject *) self);382}383```384385который присваивается члену [`tp_dealloc`](https://python-all.ru/3.6/c-api/typeobj.html#c.PyTypeObject.tp_dealloc):386387```c388.tp_dealloc = (destructor) Custom_dealloc,389```390391Этот метод сначала очищает счетчики ссылок двух атрибутов Python. [`Py_XDECREF()`](https://python-all.ru/3.6/c-api/refcounting.html#c.Py_XDECREF) корректно обрабатывает случай, когда его аргумент равен *NULL* (что может произойти, если `tp_new` не выполнился до конца). Затем он вызывает член [`tp_free`](https://python-all.ru/3.6/c-api/typeobj.html#c.PyTypeObject.tp_free) типа объекта (вычисляемый с помощью `Py_TYPE(self)`) для освобождения памяти объекта. Обратите внимание, что тип объекта может не быть `CustomType`, поскольку объект может быть экземпляром подкласса.392393> **Примечание**394>395> Явное приведение к `destructor` выше необходимо, потому что мы определили `Custom_dealloc` как принимающий аргумент `CustomObject *`, но указатель на функцию `tp_dealloc` ожидает получения аргумента `PyObject *`. В противном случае компилятор выдаст предупреждение. Это объектно-ориентированный полиморфизм в C!396397Мы хотим убедиться, что имя и фамилия инициализируются пустыми строками, поэтому мы предоставляем реализацию `tp_new`:398399```c400static PyObject *401Custom_new(PyTypeObject *type, PyObject *args, PyObject *kwds)402{403    CustomObject *self;404    self = (CustomObject *) type->tp_alloc(type, 0);405    if (self != NULL) {406        self->first = PyUnicode_FromString("");407        if (self->first == NULL) {408            Py_DECREF(self);409            return NULL;410        }411        self->last = PyUnicode_FromString("");412        if (self->last == NULL) {413            Py_DECREF(self);414            return NULL;415        }416        self->number = 0;417    }418    return (PyObject *) self;419}420```421422и устанавливаем его в член [`tp_new`](https://python-all.ru/3.6/c-api/typeobj.html#c.PyTypeObject.tp_new):423424```c425.tp_new = Custom_new,426```427428Обработчик `tp_new` отвечает за создание (в отличие от инициализации) объектов данного типа. Он доступен в Python как метод [`__new__()`](https://python-all.ru/3.6/reference/datamodel.html#object.__new__). Определять член `tp_new` не обязательно, и многие типы расширений просто переиспользуют [`PyType_GenericNew()`](https://python-all.ru/3.6/c-api/type.html#c.PyType_GenericNew), как было сделано в первой версии типа `Custom` выше. В данном случае мы используем обработчик `tp_new` для инициализации атрибутов `first` и `last` значениями по умолчанию, отличными от *NULL*.429430`tp_new` передаётся тип, экземпляр которого создаётся (не обязательно `CustomType`, если создаётся экземпляр подкласса), и любые аргументы, переданные при вызове типа; ожидается, что он вернёт созданный экземпляр. Обработчики `tp_new` всегда принимают позиционные и ключевые аргументы, но часто игнорируют их, оставляя обработку аргументов методам-инициализаторам (также известным как `tp_init` в C или `__init__` в Python).431432> **Примечание**433>434> `tp_new` не должен вызывать `tp_init` явно, так как интерпретатор сделает это сам.435436Реализация `tp_new` вызывает слот [`tp_alloc`](https://python-all.ru/3.6/c-api/typeobj.html#c.PyTypeObject.tp_alloc) для выделения памяти:437438```c439self = (CustomObject *) type->tp_alloc(type, 0);440```441442Поскольку выделение памяти может завершиться неудачей, необходимо проверить результат [`tp_alloc`](https://python-all.ru/3.6/c-api/typeobj.html#c.PyTypeObject.tp_alloc) на *NULL* перед продолжением.443444> **Примечание**445>446> Мы не заполняли слот [`tp_alloc`](https://python-all.ru/3.6/c-api/typeobj.html#c.PyTypeObject.tp_alloc) самостоятельно. Вместо этого [`PyType_Ready()`](https://python-all.ru/3.6/c-api/type.html#c.PyType_Ready) заполняет его для нас, наследуя его от базового класса, которым по умолчанию является [`object`](https://python-all.ru/3.6/library/functions.html#object). Большинство типов используют стратегию выделения по умолчанию.447448> **Примечание**449>450> При создании кооперативного [`tp_new`](https://python-all.ru/3.6/c-api/typeobj.html#c.PyTypeObject.tp_new) (такого, который вызывает [`tp_new`](https://python-all.ru/3.6/c-api/typeobj.html#c.PyTypeObject.tp_new) или [`__new__()`](https://python-all.ru/3.6/reference/datamodel.html#object.__new__) базового типа) нельзя *не* пытаться определить, какой метод вызывать, используя порядок разрешения методов во время выполнения. Всегда статически определяйте тип, который будете вызывать, и вызывайте его [`tp_new`](https://python-all.ru/3.6/c-api/typeobj.html#c.PyTypeObject.tp_new) напрямую или через `type->tp_base->tp_new`. Если этого не делать, подклассы Python вашего типа, которые также наследуются от других классов, определённых в Python, могут работать некорректно. (В частности, вы не сможете создать экземпляры таких подклассов без получения [`TypeError`](https://python-all.ru/3.6/library/exceptions.html#TypeError).)451452Мы также определяем функцию инициализации, которая принимает аргументы для предоставления начальных значений экземпляру:453454```c455static int456Custom_init(CustomObject *self, PyObject *args, PyObject *kwds)457{458    static char *kwlist[] = {"first", "last", "number", NULL};459    PyObject *first = NULL, *last = NULL, *tmp;460461    if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOi", kwlist,462                                     &first, &last,463                                     &self->number))464        return -1;465466    if (first) {467        tmp = self->first;468        Py_INCREF(first);469        self->first = first;470        Py_XDECREF(tmp);471    }472    if (last) {473        tmp = self->last;474        Py_INCREF(last);475        self->last = last;476        Py_XDECREF(tmp);477    }478    return 0;479}480```481482заполняя слот [`tp_init`](https://python-all.ru/3.6/c-api/typeobj.html#c.PyTypeObject.tp_init).483484```c485.tp_init = (initproc) Custom_init,486```487488Слот [`tp_init`](https://python-all.ru/3.6/c-api/typeobj.html#c.PyTypeObject.tp_init) открыт в Python как метод [`__init__()`](https://python-all.ru/3.6/reference/datamodel.html#object.__init__). Он используется для инициализации объекта после его создания. Инициализаторы всегда принимают позиционные и именованные аргументы и должны возвращать либо `0` в случае успеха, либо `-1` в случае ошибки.489490В отличие от обработчика `tp_new`, нет никакой гарантии, что `tp_init` будет вызван (например, модуль [`pickle`](https://python-all.ru/3.6/library/pickle.html#module-pickle) по умолчанию не вызывает [`__init__()`](https://python-all.ru/3.6/reference/datamodel.html#object.__init__) для распакованных экземпляров). Он также может быть вызван несколько раз. Кто угодно может вызвать метод [`__init__()`](https://python-all.ru/3.6/reference/datamodel.html#object.__init__) на наших объектах. По этой причине нужно быть особенно осторожным при присвоении новых значений атрибутов. Может возникнуть соблазн, например, присвоить члену `first` такое значение:491492```c493if (first) {494    Py_XDECREF(self->first);495    Py_INCREF(first);496    self->first = first;497}498```499500But 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.6/glossary.html#term-global-interpreter-lock) and let arbitrary code run in other threads that accesses and modifies our object.501502Чтобы быть параноидальными и защитить себя от такой возможности, мы почти всегда переназначаем члены перед уменьшением их счётчиков ссылок. Когда этого делать не нужно?503504- когда мы точно знаем, что счётчик ссылок больше 1;505- when we know that deallocation of the object [1](https://python-all.ru/3.6/extending/newtypes_tutorial.html#id5) will neither release the [GIL](https://python-all.ru/3.6/glossary.html#term-gil) nor cause any calls back into our type’s code;506- когда уменьшается счётчик ссылок в обработчике [`tp_dealloc`](https://python-all.ru/3.6/c-api/typeobj.html#c.PyTypeObject.tp_dealloc) для типа, который не поддерживает циклическую сборку мусора [2](https://python-all.ru/3.6/extending/newtypes_tutorial.html#id6).507508Мы хотим предоставить доступ к переменным экземпляра как к атрибутам. Есть несколько способов сделать это. Самый простой – определить определения членов:509510```c511static PyMemberDef Custom_members[] = {512    {"first", T_OBJECT_EX, offsetof(CustomObject, first), 0,513     "first name"},514    {"last", T_OBJECT_EX, offsetof(CustomObject, last), 0,515     "last name"},516    {"number", T_INT, offsetof(CustomObject, number), 0,517     "custom number"},518    {NULL}  /* Страж */519};520```521522и поместить определения в слот [`tp_members`](https://python-all.ru/3.6/c-api/typeobj.html#c.PyTypeObject.tp_members):523524```c525.tp_members = Custom_members,526```527528Каждое определение члена содержит имя члена, тип, смещение, флаги доступа и строку документации. Подробнее см. раздел [Общее управление атрибутами](https://python-all.ru/3.6/extending/newtypes.html#generic-attribute-management) ниже.529530Недостаток такого подхода в том, что он не позволяет ограничить типы объектов, которые могут быть присвоены атрибутам Python. Мы ожидаем, что имя и фамилия будут строками, но можно присвоить любые объекты Python. Более того, атрибуты можно удалять, устанавливая C-указатели в *NULL*. Даже если мы можем гарантировать, что члены инициализированы не-*NULL* значениями, эти члены могут быть установлены в *NULL*, если атрибуты удалены.531532Мы определяем один метод `Custom.name()`, который выводит имя объекта как конкатенацию имени и фамилии.533534```c535static PyObject *536Custom_name(CustomObject *self)537{538    if (self->first == NULL) {539        PyErr_SetString(PyExc_AttributeError, "first");540        return NULL;541    }542    if (self->last == NULL) {543        PyErr_SetString(PyExc_AttributeError, "last");544        return NULL;545    }546    return PyUnicode_FromFormat("%S %S", self->first, self->last);547}548```549550Метод реализован как функция на C, которая принимает экземпляр `Custom` (или подкласс `Custom`) в качестве первого аргумента. Методы всегда принимают экземпляр в качестве первого аргумента. Методы часто также принимают позиционные и именованные аргументы, но в данном случае мы не принимаем никаких и не нуждаемся в кортеже позиционных аргументов или словаре именованных аргументов. Этот метод эквивалентен следующему методу Python:551552```python553def name(self):554    return "%s %s" % (self.first, self.last)555```556557Обратите внимание, что нужно проверять возможность того, что наши члены `first` и `last` равны *NULL*. Это связано с тем, что их можно удалить, и в этом случае они устанавливаются в *NULL*. Было бы лучше запретить удаление этих атрибутов и ограничить значения атрибутов строками. Мы увидим, как это сделать в следующем разделе.558559Теперь, когда мы определили метод, нужно создать массив определений методов:560561```c562static PyMethodDef Custom_methods[] = {563    {"name", (PyCFunction) Custom_name, METH_NOARGS,564     "Return the name, combining the first and last name"565    },566    {NULL}  /* Страж */567};568```569570(обратите внимание, что мы использовали флаг [`METH_NOARGS`](https://python-all.ru/3.6/c-api/structures.html#METH_NOARGS), чтобы указать, что метод не ожидает аргументов, кроме *self*)571572и назначить его слоту [`tp_methods`](https://python-all.ru/3.6/c-api/typeobj.html#c.PyTypeObject.tp_methods):573574```c575.tp_methods = Custom_methods,576```577578Наконец, сделаем наш тип пригодным для использования в качестве базового класса для создания подклассов. Мы написали наши методы достаточно осторожно, чтобы они не делали никаких предположений о типе создаваемого или используемого объекта, поэтому всё, что нам нужно сделать, – это добавить [`Py_TPFLAGS_BASETYPE`](https://python-all.ru/3.6/c-api/typeobj.html#Py_TPFLAGS_BASETYPE) в определение флага нашего класса:579580```c581.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,582```583584Мы переименовываем `PyInit_custom()` в `PyInit_custom2()`, обновляем имя модуля в структуре [`PyModuleDef`](https://python-all.ru/3.6/c-api/module.html#c.PyModuleDef) и обновляем полное имя класса в структуре [`PyTypeObject`](https://python-all.ru/3.6/c-api/type.html#c.PyTypeObject).585586Наконец, мы обновляем наш файл `setup.py`, чтобы собрать новый модуль:587588```python589from distutils.core import setup, Extension590setup(name="custom", version="1.0",591      ext_modules=[592         Extension("custom", ["custom.c"]),593         Extension("custom2", ["custom2.c"]),594         ])595```596597## 2.3. Предоставление более точного управления атрибутами данных598599В этом разделе мы предоставим более тонкий контроль над тем, как устанавливаются атрибуты `first` и `last` в примере `Custom`. В предыдущей версии нашего модуля переменные экземпляра `first` и `last` могли быть установлены в нестроковые значения или даже удалены. Мы хотим гарантировать, что эти атрибуты всегда содержат строки.600601```c602#include <Python.h>603#include "structmember.h"604605typedef struct {606    PyObject_HEAD607    PyObject *first; /* имя */608    PyObject *last;  /* фамилия */609    int number;610} CustomObject;611612static void613Custom_dealloc(CustomObject *self)614{615    Py_XDECREF(self->first);616    Py_XDECREF(self->last);617    Py_TYPE(self)->tp_free((PyObject *) self);618}619620static PyObject *621Custom_new(PyTypeObject *type, PyObject *args, PyObject *kwds)622{623    CustomObject *self;624    self = (CustomObject *) type->tp_alloc(type, 0);625    if (self != NULL) {626        self->first = PyUnicode_FromString("");627        if (self->first == NULL) {628            Py_DECREF(self);629            return NULL;630        }631        self->last = PyUnicode_FromString("");632        if (self->last == NULL) {633            Py_DECREF(self);634            return NULL;635        }636        self->number = 0;637    }638    return (PyObject *) self;639}640641static int642Custom_init(CustomObject *self, PyObject *args, PyObject *kwds)643{644    static char *kwlist[] = {"first", "last", "number", NULL};645    PyObject *first = NULL, *last = NULL, *tmp;646647    if (!PyArg_ParseTupleAndKeywords(args, kwds, "|UUi", kwlist,648                                     &first, &last,649                                     &self->number))650        return -1;651652    if (first) {653        tmp = self->first;654        Py_INCREF(first);655        self->first = first;656        Py_DECREF(tmp);657    }658    if (last) {659        tmp = self->last;660        Py_INCREF(last);661        self->last = last;662        Py_DECREF(tmp);663    }664    return 0;665}666667static PyMemberDef Custom_members[] = {668    {"number", T_INT, offsetof(CustomObject, number), 0,669     "custom number"},670    {NULL}  /* Страж */671};672673static PyObject *674Custom_getfirst(CustomObject *self, void *closure)675{676    Py_INCREF(self->first);677    return self->first;678}679680static int681Custom_setfirst(CustomObject *self, PyObject *value, void *closure)682{683    PyObject *tmp;684    if (value == NULL) {685        PyErr_SetString(PyExc_TypeError, "Cannot delete the first attribute");686        return -1;687    }688    if (!PyUnicode_Check(value)) {689        PyErr_SetString(PyExc_TypeError,690                        "The first attribute value must be a string");691        return -1;692    }693    tmp = self->first;694    Py_INCREF(value);695    self->first = value;696    Py_DECREF(tmp);697    return 0;698}699700static PyObject *701Custom_getlast(CustomObject *self, void *closure)702{703    Py_INCREF(self->last);704    return self->last;705}706707static int708Custom_setlast(CustomObject *self, PyObject *value, void *closure)709{710    PyObject *tmp;711    if (value == NULL) {712        PyErr_SetString(PyExc_TypeError, "Cannot delete the last attribute");713        return -1;714    }715    if (!PyUnicode_Check(value)) {716        PyErr_SetString(PyExc_TypeError,717                        "The last attribute value must be a string");718        return -1;719    }720    tmp = self->last;721    Py_INCREF(value);722    self->last = value;723    Py_DECREF(tmp);724    return 0;725}726727static PyGetSetDef Custom_getsetters[] = {728    {"first", (getter) Custom_getfirst, (setter) Custom_setfirst,729     "first name", NULL},730    {"last", (getter) Custom_getlast, (setter) Custom_setlast,731     "last name", NULL},732    {NULL}  /* Страж */733};734735static PyObject *736Custom_name(CustomObject *self, PyObject *Py_UNUSED(ignored))737{738    return PyUnicode_FromFormat("%S %S", self->first, self->last);739}740741static PyMethodDef Custom_methods[] = {742    {"name", (PyCFunction) Custom_name, METH_NOARGS,743     "Return the name, combining the first and last name"744    },745    {NULL}  /* Страж */746};747748static PyTypeObject CustomType = {749    PyVarObject_HEAD_INIT(NULL, 0)750    .tp_name = "custom3.Custom",751    .tp_doc = "Custom objects",752    .tp_basicsize = sizeof(CustomObject),753    .tp_itemsize = 0,754    .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,755    .tp_new = Custom_new,756    .tp_init = (initproc) Custom_init,757    .tp_dealloc = (destructor) Custom_dealloc,758    .tp_members = Custom_members,759    .tp_methods = Custom_methods,760    .tp_getset = Custom_getsetters,761};762763static PyModuleDef custommodule = {764    PyModuleDef_HEAD_INIT,765    .m_name = "custom3",766    .m_doc = "Example module that creates an extension type.",767    .m_size = -1,768};769770PyMODINIT_FUNC771PyInit_custom3(void)772{773    PyObject *m;774    if (PyType_Ready(&CustomType) < 0)775        return NULL;776777    m = PyModule_Create(&custommodule);778    if (m == NULL)779        return NULL;780781    Py_INCREF(&CustomType);782    PyModule_AddObject(m, "Custom", (PyObject *) &CustomType);783    return m;784}785```786787Чтобы обеспечить больший контроль над атрибутами `first` и `last`, мы будем использовать пользовательские функции получения и установки. Вот функции для получения и установки атрибута `first`:788789```c790static PyObject *791Custom_getfirst(CustomObject *self, void *closure)792{793    Py_INCREF(self->first);794    return self->first;795}796797static int798Custom_setfirst(CustomObject *self, PyObject *value, void *closure)799{800    PyObject *tmp;801    if (value == NULL) {802        PyErr_SetString(PyExc_TypeError, "Cannot delete the first attribute");803        return -1;804    }805    if (!PyUnicode_Check(value)) {806        PyErr_SetString(PyExc_TypeError,807                        "The first attribute value must be a string");808        return -1;809    }810    tmp = self->first;811    Py_INCREF(value);812    self->first = value;813    Py_DECREF(tmp);814    return 0;815}816```817818Функция получения принимает объект `Custom` и «замыкание», которое является указателем void. В данном случае замыкание игнорируется. (Замыкание поддерживает расширенное использование, при котором данные определения передаются функциям получения и установки. Это может быть использовано, например, для создания единого набора функций получения и установки, которые решают, какой атрибут получить или установить на основе данных в замыкании.)819820Функция-сеттер получает объект `Custom`, новое значение и замыкание. Новое значение может быть *NULL*; в этом случае атрибут удаляется. В нашем сеттере мы вызываем ошибку, если атрибут удаляется или если его новое значение не является строкой.821822Мы создаём массив структур [`PyGetSetDef`](https://python-all.ru/3.6/c-api/structures.html#c.PyGetSetDef):823824```c825static PyGetSetDef Custom_getsetters[] = {826    {"first", (getter) Custom_getfirst, (setter) Custom_setfirst,827     "first name", NULL},828    {"last", (getter) Custom_getlast, (setter) Custom_setlast,829     "last name", NULL},830    {NULL}  /* Страж */831};832```833834и регистрируем его в слоте [`tp_getset`](https://python-all.ru/3.6/c-api/typeobj.html#c.PyTypeObject.tp_getset):835836```c837.tp_getset = Custom_getsetters,838```839840Последним элементом в структуре [`PyGetSetDef`](https://python-all.ru/3.6/c-api/structures.html#c.PyGetSetDef) является «замыкание», упомянутое выше. В данном случае мы не используем замыкание, поэтому просто передаем *NULL*.841842Также удаляем определения членов для этих атрибутов:843844```c845static PyMemberDef Custom_members[] = {846    {"number", T_INT, offsetof(CustomObject, number), 0,847     "custom number"},848    {NULL}  /* Страж */849};850```851852Также нужно обновить обработчик [`tp_init`](https://python-all.ru/3.6/c-api/typeobj.html#c.PyTypeObject.tp_init), чтобы разрешить передачу только строк [3](https://python-all.ru/3.6/extending/newtypes_tutorial.html#id7):853854```c855static int856Custom_init(CustomObject *self, PyObject *args, PyObject *kwds)857{858    static char *kwlist[] = {"first", "last", "number", NULL};859    PyObject *first = NULL, *last = NULL, *tmp;860861    if (!PyArg_ParseTupleAndKeywords(args, kwds, "|UUi", kwlist,862                                     &first, &last,863                                     &self->number))864        return -1;865866    if (first) {867        tmp = self->first;868        Py_INCREF(first);869        self->first = first;870        Py_DECREF(tmp);871    }872    if (last) {873        tmp = self->last;874        Py_INCREF(last);875        self->last = last;876        Py_DECREF(tmp);877    }878    return 0;879}880```881882С этими изменениями мы можем гарантировать, что члены `first` и `last` никогда не будут равны *NULL*, поэтому можно убрать проверки на значения *NULL* почти во всех случаях. Это означает, что большинство вызовов [`Py_XDECREF()`](https://python-all.ru/3.6/c-api/refcounting.html#c.Py_XDECREF) можно заменить на вызовы [`Py_DECREF()`](https://python-all.ru/3.6/c-api/refcounting.html#c.Py_DECREF). Единственное место, где нельзя изменить эти вызовы, – это реализация `tp_dealloc`, где есть вероятность, что инициализация этих членов не удалась в `tp_new`.883884Также переименовываем функцию инициализации модуля и имя модуля в функции инициализации, как делали раньше, и добавляем дополнительное определение в файл `setup.py`.885886## 2.4. Поддержка циклической сборки мусора887888Python имеет [циклический сборщик мусора (GC)](https://python-all.ru/3.6/glossary.html#term-garbage-collection), который может определять ненужные объекты даже при ненулевом счётчике ссылок. Это возможно, когда объекты образуют циклы. Например, рассмотрим:889890```pycon891>>> l = []892>>> l.append(l)893>>> del l894```895896В этом примере создаётся список, содержащий сам себя. При его удалении у него всё ещё остаётся ссылка на самого себя. Счётчик ссылок не падает до нуля. К счастью, циклический сборщик мусора Python в конечном итоге определит, что список является мусором, и освободит его.897898Во второй версии примера `Custom` мы разрешили хранить объекты любого типа в атрибутах `first` или `last` [4](https://python-all.ru/3.6/extending/newtypes_tutorial.html#id8). Кроме того, во второй и третьей версиях мы разрешили наследование от `Custom`, а подклассы могут добавлять произвольные атрибуты. По любой из этих двух причин объекты `Custom` могут участвовать в циклах:899900```pycon901>>> import custom3902>>> class Derived(custom3.Custom): pass903...904>>> n = Derived()905>>> n.some_attribute = n906```907908Чтобы экземпляр `Custom`, участвующий в цикле ссылок, был корректно обнаружен и собран циклическим GC, наш тип `Custom` должен заполнить два дополнительных слота и включить флаг, активирующий эти слоты:909910```c911#include <Python.h>912#include "structmember.h"913914typedef struct {915    PyObject_HEAD916    PyObject *first; /* имя */917    PyObject *last;  /* фамилия */918    int number;919} CustomObject;920921static int922Custom_traverse(CustomObject *self, visitproc visit, void *arg)923{924    Py_VISIT(self->first);925    Py_VISIT(self->last);926    return 0;927}928929static int930Custom_clear(CustomObject *self)931{932    Py_CLEAR(self->first);933    Py_CLEAR(self->last);934    return 0;935}936937static void938Custom_dealloc(CustomObject *self)939{940    PyObject_GC_UnTrack(self);941    Custom_clear(self);942    Py_TYPE(self)->tp_free((PyObject *) self);943}944945static PyObject *946Custom_new(PyTypeObject *type, PyObject *args, PyObject *kwds)947{948    CustomObject *self;949    self = (CustomObject *) type->tp_alloc(type, 0);950    if (self != NULL) {951        self->first = PyUnicode_FromString("");952        if (self->first == NULL) {953            Py_DECREF(self);954            return NULL;955        }956        self->last = PyUnicode_FromString("");957        if (self->last == NULL) {958            Py_DECREF(self);959            return NULL;960        }961        self->number = 0;962    }963    return (PyObject *) self;964}965966static int967Custom_init(CustomObject *self, PyObject *args, PyObject *kwds)968{969    static char *kwlist[] = {"first", "last", "number", NULL};970    PyObject *first = NULL, *last = NULL, *tmp;971972    if (!PyArg_ParseTupleAndKeywords(args, kwds, "|UUi", kwlist,973                                     &first, &last,974                                     &self->number))975        return -1;976977    if (first) {978        tmp = self->first;979        Py_INCREF(first);980        self->first = first;981        Py_DECREF(tmp);982    }983    if (last) {984        tmp = self->last;985        Py_INCREF(last);986        self->last = last;987        Py_DECREF(tmp);988    }989    return 0;990}991992static PyMemberDef Custom_members[] = {993    {"number", T_INT, offsetof(CustomObject, number), 0,994     "custom number"},995    {NULL}  /* Страж */996};997998static PyObject *999Custom_getfirst(CustomObject *self, void *closure)1000{1001    Py_INCREF(self->first);1002    return self->first;1003}10041005static int1006Custom_setfirst(CustomObject *self, PyObject *value, void *closure)1007{1008    if (value == NULL) {1009        PyErr_SetString(PyExc_TypeError, "Cannot delete the first attribute");1010        return -1;1011    }1012    if (!PyUnicode_Check(value)) {1013        PyErr_SetString(PyExc_TypeError,1014                        "The first attribute value must be a string");1015        return -1;1016    }1017    Py_INCREF(value);1018    Py_CLEAR(self->first);1019    self->first = value;1020    return 0;1021}10221023static PyObject *1024Custom_getlast(CustomObject *self, void *closure)1025{1026    Py_INCREF(self->last);1027    return self->last;1028}10291030static int1031Custom_setlast(CustomObject *self, PyObject *value, void *closure)1032{1033    if (value == NULL) {1034        PyErr_SetString(PyExc_TypeError, "Cannot delete the last attribute");1035        return -1;1036    }1037    if (!PyUnicode_Check(value)) {1038        PyErr_SetString(PyExc_TypeError,1039                        "The last attribute value must be a string");1040        return -1;1041    }1042    Py_INCREF(value);1043    Py_CLEAR(self->last);1044    self->last = value;1045    return 0;1046}10471048static PyGetSetDef Custom_getsetters[] = {1049    {"first", (getter) Custom_getfirst, (setter) Custom_setfirst,1050     "first name", NULL},1051    {"last", (getter) Custom_getlast, (setter) Custom_setlast,1052     "last name", NULL},1053    {NULL}  /* Страж */1054};10551056static PyObject *1057Custom_name(CustomObject *self, PyObject *Py_UNUSED(ignored))1058{1059    return PyUnicode_FromFormat("%S %S", self->first, self->last);1060}10611062static PyMethodDef Custom_methods[] = {1063    {"name", (PyCFunction) Custom_name, METH_NOARGS,1064     "Return the name, combining the first and last name"1065    },1066    {NULL}  /* Страж */1067};10681069static PyTypeObject CustomType = {1070    PyVarObject_HEAD_INIT(NULL, 0)1071    .tp_name = "custom4.Custom",1072    .tp_doc = "Custom objects",1073    .tp_basicsize = sizeof(CustomObject),1074    .tp_itemsize = 0,1075    .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,1076    .tp_new = Custom_new,1077    .tp_init = (initproc) Custom_init,1078    .tp_dealloc = (destructor) Custom_dealloc,1079    .tp_traverse = (traverseproc) Custom_traverse,1080    .tp_clear = (inquiry) Custom_clear,1081    .tp_members = Custom_members,1082    .tp_methods = Custom_methods,1083    .tp_getset = Custom_getsetters,1084};10851086static PyModuleDef custommodule = {1087    PyModuleDef_HEAD_INIT,1088    .m_name = "custom4",1089    .m_doc = "Example module that creates an extension type.",1090    .m_size = -1,1091};10921093PyMODINIT_FUNC1094PyInit_custom4(void)1095{1096    PyObject *m;1097    if (PyType_Ready(&CustomType) < 0)1098        return NULL;10991100    m = PyModule_Create(&custommodule);1101    if (m == NULL)1102        return NULL;11031104    Py_INCREF(&CustomType);1105    PyModule_AddObject(m, "Custom", (PyObject *) &CustomType);1106    return m;1107}1108```11091110Во-первых, метод обхода (traversal) сообщает циклическому GC о подобъектах, которые могут участвовать в циклах:11111112```c1113static int1114Custom_traverse(CustomObject *self, visitproc visit, void *arg)1115{1116    int vret;1117    if (self->first) {1118        vret = visit(self->first, arg);1119        if (vret != 0)1120            return vret;1121    }1122    if (self->last) {1123        vret = visit(self->last, arg);1124        if (vret != 0)1125            return vret;1126    }1127    return 0;1128}1129```11301131Для каждого подобъекта, который может участвовать в циклах, нужно вызвать функцию `visit()`, передаваемую методу обхода. Функция `visit()` принимает в качестве аргументов подобъект и дополнительный аргумент *arg*, переданный методу обхода. Она возвращает целое значение, которое должно быть возвращено, если оно ненулевое.11321133Python предоставляет макрос [`Py_VISIT()`](https://python-all.ru/3.6/c-api/gcsupport.html#c.Py_VISIT), автоматизирующий вызов функций обхода. С помощью [`Py_VISIT()`](https://python-all.ru/3.6/c-api/gcsupport.html#c.Py_VISIT) можно минимизировать количество шаблонного кода в `Custom_traverse`:11341135```c1136static int1137Custom_traverse(CustomObject *self, visitproc visit, void *arg)1138{1139    Py_VISIT(self->first);1140    Py_VISIT(self->last);1141    return 0;1142}1143```11441145> **Примечание**1146>1147> Реализация [`tp_traverse`](https://python-all.ru/3.6/c-api/typeobj.html#c.PyTypeObject.tp_traverse) должна именовать свои аргументы именно как *visit* и *arg*, чтобы использовать [`Py_VISIT()`](https://python-all.ru/3.6/c-api/gcsupport.html#c.Py_VISIT).11481149Во-вторых, нужно предоставить метод для очистки любых подобъектов, которые могут участвовать в циклах:11501151```c1152static int1153Custom_clear(CustomObject *self)1154{1155    Py_CLEAR(self->first);1156    Py_CLEAR(self->last);1157    return 0;1158}1159```11601161Обратите внимание на использование макроса [`Py_CLEAR()`](https://python-all.ru/3.6/c-api/refcounting.html#c.Py_CLEAR). Это рекомендуемый и безопасный способ очистки атрибутов данных произвольных типов с одновременным уменьшением их счетчиков ссылок. Если вместо этого вызвать [`Py_XDECREF()`](https://python-all.ru/3.6/c-api/refcounting.html#c.Py_XDECREF) для атрибута перед установкой его в *NULL*, существует вероятность, что деструктор атрибута вызовет обратно код, который снова читает атрибут (*особенно* при наличии циклической ссылки).11621163> **Примечание**1164>1165> Можно эмулировать [`Py_CLEAR()`](https://python-all.ru/3.6/c-api/refcounting.html#c.Py_CLEAR) следующим образом:1166>1167> ```c1168> PyObject *tmp;1169> tmp = self->first;1170> self->first = NULL;1171> Py_XDECREF(tmp);1172> ```1173>1174> Тем не менее, гораздо проще и менее чревато ошибками всегда использовать [`Py_CLEAR()`](https://python-all.ru/3.6/c-api/refcounting.html#c.Py_CLEAR) при удалении атрибута. Не пытайтесь микрооптимизировать в ущерб надёжности!11751176Деаллокатор `Custom_dealloc` может вызывать произвольный код при очистке атрибутов. Это означает, что циклический GC может быть запущен внутри функции. Поскольку GC предполагает, что счётчик ссылок не равен нулю, нужно отменить отслеживание объекта GC с помощью вызова [`PyObject_GC_UnTrack()`](https://python-all.ru/3.6/c-api/gcsupport.html#c.PyObject_GC_UnTrack) перед очисткой членов. Вот наша переработанная версия деаллокатора с использованием [`PyObject_GC_UnTrack()`](https://python-all.ru/3.6/c-api/gcsupport.html#c.PyObject_GC_UnTrack) и `Custom_clear`:11771178```c1179static void1180Custom_dealloc(CustomObject *self)1181{1182    PyObject_GC_UnTrack(self);1183    Custom_clear(self);1184    Py_TYPE(self)->tp_free((PyObject *) self);1185}1186```11871188Наконец, добавляем флаг [`Py_TPFLAGS_HAVE_GC`](https://python-all.ru/3.6/c-api/typeobj.html#Py_TPFLAGS_HAVE_GC) в флаги класса:11891190```c1191.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,1192```11931194Вот, в общем-то, и всё. Если бы мы написали собственные обработчики [`tp_alloc`](https://python-all.ru/3.6/c-api/typeobj.html#c.PyTypeObject.tp_alloc) или [`tp_free`](https://python-all.ru/3.6/c-api/typeobj.html#c.PyTypeObject.tp_free), их пришлось бы доработать для поддержки циклической сборки мусора. Большинство расширений используют автоматически предоставляемые версии.11951196## 2.5. Наследование от других типов11971198Можно создавать новые типы расширений, производные от существующих типов. Проще всего наследовать от встроенных типов, поскольку расширение может легко использовать нужный ему [`PyTypeObject`](https://python-all.ru/3.6/c-api/type.html#c.PyTypeObject). Совместное использование структур [`PyTypeObject`](https://python-all.ru/3.6/c-api/type.html#c.PyTypeObject) между модулями расширений может быть затруднительным.11991200В этом примере мы создадим тип `SubList`, наследующий от встроенного типа [`list`](https://python-all.ru/3.6/library/stdtypes.html#list). Новый тип будет полностью совместим с обычными списками, но будет иметь дополнительный метод `increment()`, увеличивающий внутренний счётчик:12011202```pycon1203>>> import sublist1204>>> s = sublist.SubList(range(3))1205>>> s.extend(s)1206>>> print(len(s))120761208>>> print(s.increment())120911210>>> print(s.increment())121121212```12131214```c1215#include <Python.h>12161217typedef struct {1218    PyListObject list;1219    int state;1220} SubListObject;12211222static PyObject *1223SubList_increment(SubListObject *self, PyObject *unused)1224{1225    self->state++;1226    return PyLong_FromLong(self->state);1227}12281229static PyMethodDef SubList_methods[] = {1230    {"increment", (PyCFunction) SubList_increment, METH_NOARGS,1231     PyDoc_STR("increment state counter")},1232    {NULL},1233};12341235static int1236SubList_init(SubListObject *self, PyObject *args, PyObject *kwds)1237{1238    if (PyList_Type.tp_init((PyObject *) self, args, kwds) < 0)1239        return -1;1240    self->state = 0;1241    return 0;1242}12431244static PyTypeObject SubListType = {1245    PyVarObject_HEAD_INIT(NULL, 0)1246    .tp_name = "sublist.SubList",1247    .tp_doc = "SubList objects",1248    .tp_basicsize = sizeof(SubListObject),1249    .tp_itemsize = 0,1250    .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,1251    .tp_init = (initproc) SubList_init,1252    .tp_methods = SubList_methods,1253};12541255static PyModuleDef sublistmodule = {1256    PyModuleDef_HEAD_INIT,1257    .m_name = "sublist",1258    .m_doc = "Example module that creates an extension type.",1259    .m_size = -1,1260};12611262PyMODINIT_FUNC1263PyInit_sublist(void)1264{1265    PyObject *m;1266    SubListType.tp_base = &PyList_Type;1267    if (PyType_Ready(&SubListType) < 0)1268        return NULL;12691270    m = PyModule_Create(&sublistmodule);1271    if (m == NULL)1272        return NULL;12731274    Py_INCREF(&SubListType);1275    PyModule_AddObject(m, "SubList", (PyObject *) &SubListType);1276    return m;1277}1278```12791280Как видите, исходный код очень похож на примеры `Custom` из предыдущих разделов. Разберём основные различия между ними.12811282```c1283typedef struct {1284    PyListObject list;1285    int state;1286} SubListObject;1287```12881289Основное отличие для объектов производных типов в том, что структура объекта базового типа должна быть первым значением. Базовый тип уже включает [`PyObject_HEAD()`](https://python-all.ru/3.6/c-api/structures.html#c.PyObject_HEAD) в начале своей структуры.12901291Когда объект Python является экземпляром `SubList`, его указатель `PyObject *` можно безопасно приводить как к `PyListObject *`, так и к `SubListObject *`:12921293```c1294static int1295SubList_init(SubListObject *self, PyObject *args, PyObject *kwds)1296{1297    if (PyList_Type.tp_init((PyObject *) self, args, kwds) < 0)1298        return -1;1299    self->state = 0;1300    return 0;1301}1302```13031304Выше показано, как вызывать метод `__init__` базового типа.13051306Этот шаблон важен при написании типа с пользовательскими членами [`tp_new`](https://python-all.ru/3.6/c-api/typeobj.html#c.PyTypeObject.tp_new) и [`tp_dealloc`](https://python-all.ru/3.6/c-api/typeobj.html#c.PyTypeObject.tp_dealloc). Обработчик [`tp_new`](https://python-all.ru/3.6/c-api/typeobj.html#c.PyTypeObject.tp_new) не должен фактически выделять память для объекта с помощью своего [`tp_alloc`](https://python-all.ru/3.6/c-api/typeobj.html#c.PyTypeObject.tp_alloc), а должен позволить базовому классу обработать это, вызвав его собственный [`tp_new`](https://python-all.ru/3.6/c-api/typeobj.html#c.PyTypeObject.tp_new).13071308Структура [`PyTypeObject`](https://python-all.ru/3.6/c-api/type.html#c.PyTypeObject) поддерживает [`tp_base`](https://python-all.ru/3.6/c-api/typeobj.html#c.PyTypeObject.tp_base) указывающее конкретный базовый класс типа. Из-за проблем с кроссплатформенными компиляторами нельзя заполнить это поле напрямую ссылкой на [`PyList_Type`](https://python-all.ru/3.6/c-api/list.html#c.PyList_Type); это следует сделать позже, в функции инициализации модуля:13091310```c1311PyMODINIT_FUNC1312PyInit_sublist(void)1313{1314    PyObject* m;1315    SubListType.tp_base = &PyList_Type;1316    if (PyType_Ready(&SubListType) < 0)1317        return NULL;13181319    m = PyModule_Create(&sublistmodule);1320    if (m == NULL)1321        return NULL;13221323    Py_INCREF(&SubListType);1324    PyModule_AddObject(m, "SubList", (PyObject *) &SubListType);1325    return m;1326}1327```13281329Перед вызовом [`PyType_Ready()`](https://python-all.ru/3.6/c-api/type.html#c.PyType_Ready) структура типа должна иметь заполненный слот [`tp_base`](https://python-all.ru/3.6/c-api/typeobj.html#c.PyTypeObject.tp_base). При наследовании от существующего типа необязательно заполнять слот [`tp_alloc`](https://python-all.ru/3.6/c-api/typeobj.html#c.PyTypeObject.tp_alloc) значением [`PyType_GenericNew()`](https://python-all.ru/3.6/c-api/type.html#c.PyType_GenericNew) – функция выделения памяти из базового типа будет унаследована.13301331После этого вызов [`PyType_Ready()`](https://python-all.ru/3.6/c-api/type.html#c.PyType_Ready) и добавление объекта типа в модуль выполняется так же, как в базовых примерах `Custom`.13321333Сноски13341335**[1](https://python-all.ru/3.6/extending/newtypes_tutorial.html#id1)**13361337Это верно, когда известно, что объект относится к базовому типу, например, строке или числу с плавающей запятой.13381339**[2](https://python-all.ru/3.6/extending/newtypes_tutorial.html#id2)**13401341На это полагались в обработчике [`tp_dealloc`](https://python-all.ru/3.6/c-api/typeobj.html#c.PyTypeObject.tp_dealloc) в данном примере, поскольку тип не поддерживает сборку мусора.13421343**[3](https://python-all.ru/3.6/extending/newtypes_tutorial.html#id3)**13441345Теперь известно, что первый и последний элементы являются строками, поэтому можно было бы менее осторожно уменьшать их счётчики ссылок. Однако допускаются экземпляры подклассов строк. Даже если освобождение обычных строк не вызовет обратного вызова в наши объекты, нельзя гарантировать, что освобождение экземпляра подкласса строки не вызовет такого обратного вызова.13461347**[4](https://python-all.ru/3.6/extending/newtypes_tutorial.html#id4)**13481349Кроме того, даже если атрибуты ограничены экземплярами строк, пользователь может передать произвольные подклассы [`str`](https://python-all.ru/3.6/library/stdtypes.html#str) и тем самым всё равно создать циклические ссылки.1350