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

newtypes.md

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

1> **Источник:** https://python-all.ru/3.11/extending/newtypes.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# 3. Определение типов расширений: разные темы89Этот раздел представляет краткий обзор различных методов типа, которые можно реализовать, и их назначения.1011Вот определение [`PyTypeObject`](https://python-all.ru/3.11/c-api/type.html#c.PyTypeObject), из которого исключены некоторые поля, используемые только в [отладочных сборках](https://python-all.ru/3.11/using/configure.html#debug-build):1213```c14typedef struct _typeobject {15    PyObject_VAR_HEAD16    const char *tp_name; /* Для вывода в формате "<module>.<name>" */17    Py_ssize_t tp_basicsize, tp_itemsize; /* Для выделения памяти */1819    /* Методы для реализации стандартных операций */2021    destructor tp_dealloc;22    Py_ssize_t tp_vectorcall_offset;23    getattrfunc tp_getattr;24    setattrfunc tp_setattr;25    PyAsyncMethods *tp_as_async; /* ранее известный как tp_compare (Python 2)26                                    или tp_reserved (Python 3) */27    reprfunc tp_repr;2829    /* Наборы методов для стандартных классов */3031    PyNumberMethods *tp_as_number;32    PySequenceMethods *tp_as_sequence;33    PyMappingMethods *tp_as_mapping;3435    /* Дополнительные стандартные операции (здесь для двоичной совместимости) */3637    hashfunc tp_hash;38    ternaryfunc tp_call;39    reprfunc tp_str;40    getattrofunc tp_getattro;41    setattrofunc tp_setattro;4243    /* Функции для доступа к объекту как к буферу ввода/вывода */44    PyBufferProcs *tp_as_buffer;4546    /* Флаги для определения наличия опциональных/расширенных возможностей */47    unsigned long tp_flags;4849    const char *tp_doc; /* Строка документации */5051    /* Назначенное значение в версии 2.0 */52    /* вызов функции для всех доступных объектов */53    traverseproc tp_traverse;5455    /* удаление ссылок на содержащиеся объекты */56    inquiry tp_clear;5758    /* Назначенное значение в версии 2.1 */59    /* расширенные сравнения */60    richcmpfunc tp_richcompare;6162    /* включение слабых ссылок */63    Py_ssize_t tp_weaklistoffset;6465    /* Итераторы */66    getiterfunc tp_iter;67    iternextfunc tp_iternext;6869    /* Дескриптор атрибутов и механизмы подклассов */70    struct PyMethodDef *tp_methods;71    struct PyMemberDef *tp_members;72    struct PyGetSetDef *tp_getset;73    // Сильная ссылка на тип в куче, заимствованная ссылка на статический тип74    struct _typeobject *tp_base;75    PyObject *tp_dict;76    descrgetfunc tp_descr_get;77    descrsetfunc tp_descr_set;78    Py_ssize_t tp_dictoffset;79    initproc tp_init;80    allocfunc tp_alloc;81    newfunc tp_new;82    freefunc tp_free; /* Низкоуровневая процедура освобождения памяти */83    inquiry tp_is_gc; /* Для PyObject_IS_GC */84    PyObject *tp_bases;85    PyObject *tp_mro; /* порядок разрешения методов */86    PyObject *tp_cache;87    PyObject *tp_subclasses;88    PyObject *tp_weaklist;89    destructor tp_del;9091    /* Метка версии кэша атрибутов типа. Добавлено в версии 2.6. */92    unsigned int tp_version_tag;9394    destructor tp_finalize;95    vectorcallfunc tp_vectorcall;96} PyTypeObject;97```9899Это целое *множество* методов. Но не стоит слишком беспокоиться: если нужно определить тип, скорее всего, потребуется реализовать лишь некоторые из них.100101Как вы, вероятно, уже ожидаете, мы рассмотрим это и предоставим дополнительную информацию о различных обработчиках. Мы не будем следовать порядку их определения в структуре, поскольку на порядок полей сильно повлияло историческое наследие. Чаще всего проще найти пример, содержащий нужные поля, а затем изменить их значения под свой новый тип.102103```c104const char *tp_name; /* Для вывода */105```106107Имя типа – как упоминалось в предыдущей главе, оно будет появляться в разных местах, в основном для диагностики. Постарайтесь выбрать что-то, что будет полезно в такой ситуации!108109```c110Py_ssize_t tp_basicsize, tp_itemsize; /* Для выделения памяти */111```112113Эти поля сообщают среде выполнения, сколько памяти выделять при создании новых объектов этого типа. В Python есть встроенная поддержка структур переменной длины (например, строки, кортежи), для чего и предназначено поле [`tp_itemsize`](https://python-all.ru/3.11/c-api/typeobj.html#c.PyTypeObject.tp_itemsize). Оно будет рассмотрено позже.114115```c116const char *tp_doc;117```118119Здесь можно указать строку (или её адрес), которая будет возвращена, когда скрипт Python обратится к `obj.__doc__` для получения строки документации.120121Теперь перейдём к основным методам типа – тем, которые будут реализованы в большинстве типов-расширений.122123## 3.1. Завершение и освобождение памяти124125```c126destructor tp_dealloc;127```128129Эта функция вызывается, когда счётчик ссылок на экземпляр вашего типа уменьшается до нуля и интерпретатор Python хочет освободить его. Если ваш тип требует освобождения памяти или другой очистки, этот код можно разместить здесь. Сам объект также должен быть освобождён здесь. Вот пример такой функции:130131```c132static void133newdatatype_dealloc(newdatatypeobject *obj)134{135    free(obj->obj_UnderlyingDatatypePtr);136    Py_TYPE(obj)->tp_free((PyObject *)obj);137}138```139140Если ваш тип поддерживает сборку мусора, деструктор должен вызвать [`PyObject_GC_UnTrack()`](https://python-all.ru/3.11/c-api/gcsupport.html#c.PyObject_GC_UnTrack) перед очисткой любых полей-членов:141142```c143static void144newdatatype_dealloc(newdatatypeobject *obj)145{146    PyObject_GC_UnTrack(obj);147    Py_CLEAR(obj->other_obj);148    ...149    Py_TYPE(obj)->tp_free((PyObject *)obj);150}151```152153Важное требование к функции деаллокатора: она не должна вмешиваться в уже установленные исключения. Это важно, поскольку деаллокаторы часто вызываются при раскрутке стека Python; когда стек раскручивается из-за исключения (а не при обычном возврате), ничего не делается для защиты деаллокаторов от того, что исключение уже установлено. Любые действия деаллокатора, которые могут вызвать выполнение дополнительного кода Python, могут обнаружить установленное исключение. Это может привести к вводящим в заблуждение ошибкам интерпретатора. Правильный способ защиты – сохранить текущее исключение перед выполнением небезопасного действия и восстановить его после завершения. Это можно сделать с помощью функций [`PyErr_Fetch()`](https://python-all.ru/3.11/c-api/exceptions.html#c.PyErr_Fetch) и [`PyErr_Restore()`](https://python-all.ru/3.11/c-api/exceptions.html#c.PyErr_Restore):154155```c156static void157my_dealloc(PyObject *obj)158{159    MyObject *self = (MyObject *) obj;160    PyObject *cbresult;161162    if (self->my_callback != NULL) {163        PyObject *err_type, *err_value, *err_traceback;164165        /* Сохраняет текущее состояние исключения */166        PyErr_Fetch(&err_type, &err_value, &err_traceback);167168        cbresult = PyObject_CallNoArgs(self->my_callback);169        if (cbresult == NULL)170            PyErr_WriteUnraisable(self->my_callback);171        else172            Py_DECREF(cbresult);173174        /* Восстанавливает сохранённое состояние исключения */175        PyErr_Restore(err_type, err_value, err_traceback);176177        Py_DECREF(self->my_callback);178    }179    Py_TYPE(obj)->tp_free((PyObject*)self);180}181```182183> **Примечание**184>185> Существуют ограничения на то, что можно безопасно делать в функции деаллокатора. Во-первых, если ваш тип поддерживает сборку мусора (с помощью [`tp_traverse`](https://python-all.ru/3.11/c-api/typeobj.html#c.PyTypeObject.tp_traverse) и/или [`tp_clear`](https://python-all.ru/3.11/c-api/typeobj.html#c.PyTypeObject.tp_clear)), некоторые члены объекта могут быть очищены или финализированы к моменту вызова [`tp_dealloc`](https://python-all.ru/3.11/c-api/typeobj.html#c.PyTypeObject.tp_dealloc). Во-вторых, в [`tp_dealloc`](https://python-all.ru/3.11/c-api/typeobj.html#c.PyTypeObject.tp_dealloc) объект находится в нестабильном состоянии: его счётчик ссылок равен нулю. Любой вызов нетривиального объекта или API (как в примере выше) может снова вызвать [`tp_dealloc`](https://python-all.ru/3.11/c-api/typeobj.html#c.PyTypeObject.tp_dealloc), что приведёт к двойному освобождению и аварийному завершению.186>187> Начиная с Python 3.4, рекомендуется не помещать сложный код финализации в [`tp_dealloc`](https://python-all.ru/3.11/c-api/typeobj.html#c.PyTypeObject.tp_dealloc), а вместо этого использовать новый метод типа [`tp_finalize`](https://python-all.ru/3.11/c-api/typeobj.html#c.PyTypeObject.tp_finalize).188>189> > **См. также**190> >191> > [**PEP 442**](https://python-all.ru/3.11/extending/newtypes.html) описывает новую схему финализации.192193## 3.2. Представление объектов194195В Python есть два способа создать текстовое представление объекта: функция [`repr()`](https://python-all.ru/3.11/library/functions.html#repr) и функция [`str()`](https://python-all.ru/3.11/library/stdtypes.html#str). (Функция [`print()`](https://python-all.ru/3.11/library/functions.html#print) просто вызывает [`str()`](https://python-all.ru/3.11/library/stdtypes.html#str).) Оба этих обработчика необязательны.196197```c198reprfunc tp_repr;199reprfunc tp_str;200```201202Обработчик [`tp_repr`](https://python-all.ru/3.11/c-api/typeobj.html#c.PyTypeObject.tp_repr) должен возвращать строковый объект, содержащий представление экземпляра, для которого он вызван. Вот простой пример:203204```c205static PyObject *206newdatatype_repr(newdatatypeobject * obj)207{208    return PyUnicode_FromFormat("Repr-ified_newdatatype{{size:%d}}",209                                obj->obj_UnderlyingDatatypePtr->size);210}211```212213Если обработчик [`tp_repr`](https://python-all.ru/3.11/c-api/typeobj.html#c.PyTypeObject.tp_repr) не указан, интерпретатор предоставит представление, использующее [`tp_name`](https://python-all.ru/3.11/c-api/typeobj.html#c.PyTypeObject.tp_name) типа и уникальное идентифицирующее значение объекта.214215Обработчик [`tp_str`](https://python-all.ru/3.11/c-api/typeobj.html#c.PyTypeObject.tp_str) относится к [`str()`](https://python-all.ru/3.11/library/stdtypes.html#str) так же, как описанный выше обработчик [`tp_repr`](https://python-all.ru/3.11/c-api/typeobj.html#c.PyTypeObject.tp_repr) относится к [`repr()`](https://python-all.ru/3.11/library/functions.html#repr): он вызывается, когда код Python вызывает [`str()`](https://python-all.ru/3.11/library/stdtypes.html#str) для экземпляра вашего объекта. Его реализация очень похожа на функцию [`tp_repr`](https://python-all.ru/3.11/c-api/typeobj.html#c.PyTypeObject.tp_repr), но итоговая строка предназначена для чтения человеком. Если [`tp_str`](https://python-all.ru/3.11/c-api/typeobj.html#c.PyTypeObject.tp_str) не указан, используется обработчик [`tp_repr`](https://python-all.ru/3.11/c-api/typeobj.html#c.PyTypeObject.tp_repr).216217Вот простой пример:218219```c220static PyObject *221newdatatype_str(newdatatypeobject * obj)222{223    return PyUnicode_FromFormat("Stringified_newdatatype{{size:%d}}",224                                obj->obj_UnderlyingDatatypePtr->size);225}226```227228## 3.3. Управление атрибутами229230Для каждого объекта, который может поддерживать атрибуты, соответствующий тип должен предоставлять функции, управляющие разрешением атрибутов. Нужна функция, которая может получать атрибуты (если они определены), и другая – для установки атрибутов (если установка разрешена). Удаление атрибута – особый случай: новое значение, передаваемое обработчику, равно `NULL`.231232Python поддерживает две пары обработчиков атрибутов; типу, поддерживающему атрибуты, нужно реализовать функции только для одной пары. Разница в том, что одна пара принимает имя атрибута как char\*, а другая – как [PyObject](https://python-all.ru/3.11/c-api/structures.html#c.PyObject)\*. Каждый тип может использовать ту пару, которая удобнее для реализации.233234```c235getattrfunc  tp_getattr;        /* char * version */236setattrfunc  tp_setattr;237/* ... */238getattrofunc tp_getattro;       /* PyObject * version */239setattrofunc tp_setattro;240```241242Если доступ к атрибутам объекта всегда является простой операцией (это будет объяснено далее), существуют общие реализации, которые можно использовать для предоставления версии функций управления атрибутами [PyObject](https://python-all.ru/3.11/c-api/structures.html#c.PyObject)\*. Реальная потребность в типоспецифичных обработчиках атрибутов практически полностью исчезла начиная с Python 2.2, хотя есть много примеров, которые не были обновлены для использования нового общего механизма.243244### 3.3.1. Общее управление атрибутами245246Большинство типов расширений используют только *простые* атрибуты. Что делает атрибуты простыми? Нужно выполнить лишь несколько условий:2472481. Имена атрибутов должны быть известны на момент вызова [`PyType_Ready()`](https://python-all.ru/3.11/c-api/type.html#c.PyType_Ready).2492. Не требуется специальной обработки для фиксации факта поиска или установки атрибута, и не нужно предпринимать действий в зависимости от значения.250251Обратите внимание, что этот список не накладывает никаких ограничений на значения атрибутов, момент их вычисления или способ хранения соответствующих данных.252253При вызове [`PyType_Ready()`](https://python-all.ru/3.11/c-api/type.html#c.PyType_Ready) используются три таблицы, на которые ссылается объект типа, для создания [дескрипторов](https://python-all.ru/3.11/glossary.html#term-descriptor), помещаемых в словарь объекта типа. Каждый дескриптор управляет доступом к одному атрибуту объекта-экземпляра. Каждая из таблиц необязательна; если все три равны `NULL`, экземпляры типа будут иметь только атрибуты, унаследованные от базового типа, и должны оставить поля [`tp_getattro`](https://python-all.ru/3.11/c-api/typeobj.html#c.PyTypeObject.tp_getattro) и [`tp_setattro`](https://python-all.ru/3.11/c-api/typeobj.html#c.PyTypeObject.tp_setattro) равными `NULL`, позволяя базовому типу обрабатывать атрибуты.254255Таблицы объявлены как три поля объекта типа:256257```c258struct PyMethodDef *tp_methods;259struct PyMemberDef *tp_members;260struct PyGetSetDef *tp_getset;261```262263Если [`tp_methods`](https://python-all.ru/3.11/c-api/typeobj.html#c.PyTypeObject.tp_methods) не равно `NULL`, оно должно указывать на массив структур [`PyMethodDef`](https://python-all.ru/3.11/c-api/structures.html#c.PyMethodDef). Каждая запись в таблице является экземпляром этой структуры:264265```c266typedef struct PyMethodDef {267    const char  *ml_name;       /* имя метода */268    PyCFunction  ml_meth;       /* функция реализации */269    int          ml_flags;      /* флаги */270    const char  *ml_doc;        /* докстринг */271} PyMethodDef;272```273274Для каждого метода, предоставляемого типом, должна быть определена одна запись; для методов, унаследованных от базового типа, записи не нужны. В конце требуется одна дополнительная запись – сигнальная метка, обозначающая конец массива. Поле [`ml_name`](https://python-all.ru/3.11/c-api/structures.html#c.PyMethodDef.ml_name) сигнальной метки должно быть равно `NULL`.275276Вторая таблица используется для определения атрибутов, которые напрямую отображаются на данные, хранящиеся в экземпляре. Поддерживаются различные примитивные типы C, доступ может быть только для чтения или для чтения и записи. Структуры в таблице определены так:277278```c279typedef struct PyMemberDef {280    const char *name;281    int         type;282    int         offset;283    int         flags;284    const char *doc;285} PyMemberDef;286```287288Для каждой записи в таблице будет создан [дескриптор](https://python-all.ru/3.11/glossary.html#term-descriptor) и добавлен к типу, который сможет извлекать значение из структуры экземпляра. Поле `type` должно содержать один из кодов типов, определённых в заголовке `structmember.h`; значение будет использоваться для определения того, как преобразовывать значения Python в значения C и обратно. Поле `flags` используется для хранения флагов, управляющих доступом к атрибуту.289290Следующие константы флагов определены в `structmember.h`; их можно комбинировать с помощью побитового ИЛИ.291292| Константа | Значение |293| --- | --- |294| `READONLY` | Никогда не доступен для записи. |295| `PY_AUDIT_READ` | Порождает `object.__getattr__` [события аудита](https://python-all.ru/3.11/library/audit_events.html#audit-events) перед чтением. |296297Изменено в версии 3.10: `RESTRICTED`, `READ_RESTRICTED` и `WRITE_RESTRICTED` устарели. Однако `READ_RESTRICTED` является псевдонимом для `PY_AUDIT_READ`, поэтому поля, указывающие `RESTRICTED` или `READ_RESTRICTED`, также будут вызывать событие аудита.298299Интересное преимущество использования таблицы [`tp_members`](https://python-all.ru/3.11/c-api/typeobj.html#c.PyTypeObject.tp_members) для создания дескрипторов, используемых во время выполнения, заключается в том, что любой атрибут, определённый таким образом, может иметь связанную строку документации – достаточно просто указать текст в таблице. Приложение может использовать API интроспекции для получения дескриптора из объекта класса и получить строку документации через его атрибут `__doc__`.300301Как и в таблице [`tp_methods`](https://python-all.ru/3.11/c-api/typeobj.html#c.PyTypeObject.tp_methods), требуется сигнальная запись со значением [`ml_name`](https://python-all.ru/3.11/c-api/structures.html#c.PyMethodDef.ml_name) равным `NULL`.302303### 3.3.2. Управление атрибутами для конкретного типа304305Для простоты здесь будет показана только версия с char\*; единственное различие между вариантами интерфейса char\* и [PyObject](https://python-all.ru/3.11/c-api/structures.html#c.PyObject)\* заключается в типе параметра name. Этот пример делает по сути то же самое, что и общий пример выше, но не использует общую поддержку, добавленную в Python 2.2. В нём объясняется, как вызываются функции-обработчики, чтобы, если понадобится расширить их функциональность, было понятно, что нужно делать.306307Обработчик [`tp_getattr`](https://python-all.ru/3.11/c-api/typeobj.html#c.PyTypeObject.tp_getattr) вызывается, когда объекту требуется поиск атрибута. Он вызывается в тех же ситуациях, что и метод [`__getattr__()`](https://python-all.ru/3.11/reference/datamodel.html#object.__getattr__) класса.308309Вот пример:310311```c312static PyObject *313newdatatype_getattr(newdatatypeobject *obj, char *name)314{315    if (strcmp(name, "data") == 0)316    {317        return PyLong_FromLong(obj->data);318    }319320    PyErr_Format(PyExc_AttributeError,321                 "'%.50s' object has no attribute '%.400s'",322                 tp->tp_name, name);323    return NULL;324}325```326327Обработчик [`tp_setattr`](https://python-all.ru/3.11/c-api/typeobj.html#c.PyTypeObject.tp_setattr) вызывается, когда вызывается метод [`__setattr__()`](https://python-all.ru/3.11/reference/datamodel.html#object.__setattr__) или [`__delattr__()`](https://python-all.ru/3.11/reference/datamodel.html#object.__delattr__) экземпляра класса. Когда атрибут должен быть удалён, третий параметр будет равен `NULL`. Вот пример, который просто возбуждает исключение; если бы это было всё, что требуется, обработчик [`tp_setattr`](https://python-all.ru/3.11/c-api/typeobj.html#c.PyTypeObject.tp_setattr) следовало бы установить в `NULL`.328329```c330static int331newdatatype_setattr(newdatatypeobject *obj, char *name, PyObject *v)332{333    PyErr_Format(PyExc_RuntimeError, "Read-only attribute: %s", name);334    return -1;335}336```337338## 3.4. Сравнение объектов339340```c341richcmpfunc tp_richcompare;342```343344Обработчик [`tp_richcompare`](https://python-all.ru/3.11/c-api/typeobj.html#c.PyTypeObject.tp_richcompare) вызывается, когда требуется сравнение. Он аналогичен [методам расширенного сравнения](https://python-all.ru/3.11/reference/datamodel.html#richcmpfuncs), таким как `__lt__()`, а также вызывается [`PyObject_RichCompare()`](https://python-all.ru/3.11/c-api/object.html#c.PyObject_RichCompare) и [`PyObject_RichCompareBool()`](https://python-all.ru/3.11/c-api/object.html#c.PyObject_RichCompareBool).345346Эта функция вызывается с двумя объектами Python и оператором в качестве аргументов; оператором может быть `Py_EQ`, `Py_NE`, `Py_LE`, `Py_GE`, `Py_LT` или `Py_GT`. Она должна сравнить два объекта в соответствии с указанным оператором и вернуть `Py_True` или `Py_False` при успешном сравнении, `Py_NotImplemented` чтобы указать, что сравнение не реализовано и следует попробовать метод сравнения другого объекта, или `NULL`, если было установлено исключение.347348Вот пример реализации для типа данных, который считается равным, если размер внутреннего указателя одинаков:349350```c351static PyObject *352newdatatype_richcmp(PyObject *obj1, PyObject *obj2, int op)353{354    PyObject *result;355    int c, size1, size2;356357    /* код для проверки, что оба аргумента имеют нужный тип358       newdatatype опущен */359360    size1 = obj1->obj_UnderlyingDatatypePtr->size;361    size2 = obj2->obj_UnderlyingDatatypePtr->size;362363    switch (op) {364    case Py_LT: c = size1 <  size2; break;365    case Py_LE: c = size1 <= size2; break;366    case Py_EQ: c = size1 == size2; break;367    case Py_NE: c = size1 != size2; break;368    case Py_GT: c = size1 >  size2; break;369    case Py_GE: c = size1 >= size2; break;370    }371    result = c ? Py_True : Py_False;372    Py_INCREF(result);373    return result;374 }375```376377## 3.5. Поддержка абстрактных протоколов378379Python поддерживает множество *абстрактных* «протоколов»; конкретные интерфейсы для их использования описаны в разделе [Уровень абстрактных объектов](https://python-all.ru/3.11/c-api/abstract.html#abstract).380381Некоторые из этих абстрактных интерфейсов были определены на ранних этапах разработки реализации Python. В частности, протоколы чисел, отображений и последовательностей были частью Python с самого начала. Со временем были добавлены и другие протоколы. Для протоколов, которые зависят от нескольких процедур-обработчиков из реализации типа, старые протоколы были определены как необязательные блоки обработчиков, на которые ссылается объект типа. Для более новых протоколов в основном объекте типа есть дополнительные слоты, при этом устанавливается бит флага, указывающий на наличие слотов, которые должен проверять интерпретатор. (Бит флага не указывает, что значения слотов не равны `NULL`. Флаг может быть установлен для обозначения наличия слота, но слот может оставаться незаполненным.)382383```c384PyNumberMethods   *tp_as_number;385PySequenceMethods *tp_as_sequence;386PyMappingMethods  *tp_as_mapping;387```388389Если требуется, чтобы объект мог вести себя как число, последовательность или отображение, нужно поместить адрес структуры, реализующей C-тип [`PyNumberMethods`](https://python-all.ru/3.11/c-api/typeobj.html#c.PyNumberMethods), [`PySequenceMethods`](https://python-all.ru/3.11/c-api/typeobj.html#c.PySequenceMethods) или [`PyMappingMethods`](https://python-all.ru/3.11/c-api/typeobj.html#c.PyMappingMethods) соответственно. Заполнять эту структуру подходящими значениями – ваша задача. Примеры использования каждой из них можно найти в каталоге `Objects` дистрибутива исходного кода Python.390391```c392hashfunc tp_hash;393```394395Эта функция, если вы решите её предоставить, должна возвращать хеш-число для экземпляра вашего типа данных. Вот простой пример:396397```c398static Py_hash_t399newdatatype_hash(newdatatypeobject *obj)400{401    Py_hash_t result;402    result = obj->some_size + 32767 * obj->some_number;403    if (result == -1)404       result = -2;405    return result;406}407```408409`Py_hash_t` – знаковый целочисленный тип с шириной, зависящей от платформы. Возврат `-1` из [`tp_hash`](https://python-all.ru/3.11/c-api/typeobj.html#c.PyTypeObject.tp_hash) указывает на ошибку, поэтому следует избегать его возврата при успешном вычислении хеша, как показано выше.410411```c412ternaryfunc tp_call;413```414415Эта функция вызывается, когда экземпляр вашего типа данных «вызывается», например, если `obj1` является экземпляром вашего типа данных и скрипт Python содержит `obj1('hello')`, то вызывается обработчик [`tp_call`](https://python-all.ru/3.11/c-api/typeobj.html#c.PyTypeObject.tp_call).416417Эта функция принимает три аргумента:4184191. *self* – это экземпляр типа данных, для которого выполняется вызов. Если вызов имеет вид `obj1('hello')`, то *self* равно `obj1`.4202. *args* – кортеж с аргументами вызова. Для извлечения аргументов можно использовать [`PyArg_ParseTuple()`](https://python-all.ru/3.11/c-api/arg.html#c.PyArg_ParseTuple).4213. *kwds* – словарь переданных именованных аргументов. Если он не равен `NULL` и вы поддерживаете именованные аргументы, используйте [`PyArg_ParseTupleAndKeywords()`](https://python-all.ru/3.11/c-api/arg.html#c.PyArg_ParseTupleAndKeywords) для их извлечения. Если вы не хотите поддерживать именованные аргументы и этот словарь не равен `NULL`, возбудите исключение [`TypeError`](https://python-all.ru/3.11/library/exceptions.html#TypeError) с сообщением о том, что именованные аргументы не поддерживаются.422423Вот игрушечная реализация `tp_call`:424425```c426static PyObject *427newdatatype_call(newdatatypeobject *self, PyObject *args, PyObject *kwds)428{429    PyObject *result;430    const char *arg1;431    const char *arg2;432    const char *arg3;433434    if (!PyArg_ParseTuple(args, "sss:call", &arg1, &arg2, &arg3)) {435        return NULL;436    }437    result = PyUnicode_FromFormat(438        "Returning -- value: [%d] arg1: [%s] arg2: [%s] arg3: [%s]\n",439        obj->obj_UnderlyingDatatypePtr->size,440        arg1, arg2, arg3);441    return result;442}443```444445```c446/* Итераторы */447getiterfunc tp_iter;448iternextfunc tp_iternext;449```450451Эти функции обеспечивают поддержку протокола итератора. Оба обработчика принимают ровно один параметр – экземпляр, для которого они вызываются, и возвращают новую ссылку. В случае ошибки они должны установить исключение и вернуть `NULL`. [`tp_iter`](https://python-all.ru/3.11/c-api/typeobj.html#c.PyTypeObject.tp_iter) соответствует методу Python [`__iter__()`](https://python-all.ru/3.11/reference/datamodel.html#object.__iter__), а [`tp_iternext`](https://python-all.ru/3.11/c-api/typeobj.html#c.PyTypeObject.tp_iternext) – методу Python [`__next__()`](https://python-all.ru/3.11/library/stdtypes.html#iterator.__next__).452453Любой [итерируемый](https://python-all.ru/3.11/glossary.html#term-iterable) объект должен реализовывать обработчик [`tp_iter`](https://python-all.ru/3.11/c-api/typeobj.html#c.PyTypeObject.tp_iter), который должен возвращать [итератор](https://python-all.ru/3.11/glossary.html#term-iterator). Здесь действуют те же правила, что и для классов Python:454455- Для коллекций (например, списков и кортежей), которые могут поддерживать несколько независимых итераторов, при каждом вызове [`tp_iter`](https://python-all.ru/3.11/c-api/typeobj.html#c.PyTypeObject.tp_iter) следует создавать и возвращать новый итератор.456- Объекты, которые можно обойти только один раз (обычно из-за побочных эффектов итерации, например файловые объекты), могут реализовать [`tp_iter`](https://python-all.ru/3.11/c-api/typeobj.html#c.PyTypeObject.tp_iter), возвращая новую ссылку на себя – и, следовательно, также должны реализовать обработчик [`tp_iternext`](https://python-all.ru/3.11/c-api/typeobj.html#c.PyTypeObject.tp_iternext).457458Любой объект [итератор](https://python-all.ru/3.11/glossary.html#term-iterator) должен реализовывать как [`tp_iter`](https://python-all.ru/3.11/c-api/typeobj.html#c.PyTypeObject.tp_iter), так и [`tp_iternext`](https://python-all.ru/3.11/c-api/typeobj.html#c.PyTypeObject.tp_iternext). Обработчик [`tp_iter`](https://python-all.ru/3.11/c-api/typeobj.html#c.PyTypeObject.tp_iter) итератора должен возвращать новую ссылку на итератор. Его обработчик [`tp_iternext`](https://python-all.ru/3.11/c-api/typeobj.html#c.PyTypeObject.tp_iternext) должен возвращать новую ссылку на следующий объект в итерации, если таковой имеется. Если итерация завершена, [`tp_iternext`](https://python-all.ru/3.11/c-api/typeobj.html#c.PyTypeObject.tp_iternext) может вернуть `NULL` без установки исключения или установить [`StopIteration`](https://python-all.ru/3.11/library/exceptions.html#StopIteration) *в дополнение* к возврату `NULL`; отказ от исключения может дать немного лучшую производительность. Если произошла настоящая ошибка, [`tp_iternext`](https://python-all.ru/3.11/c-api/typeobj.html#c.PyTypeObject.tp_iternext) всегда должен установить исключение и вернуть `NULL`.459460## 3.6. Поддержка слабых ссылок461462Одна из целей реализации слабых ссылок в Python – позволить любому типу участвовать в механизме слабых ссылок без дополнительных накладных расходов для критичных к производительности объектов (например, чисел).463464> **См. также**465>466> Документация по модулю [`weakref`](https://python-all.ru/3.11/library/weakref.html#module-weakref).467468Чтобы объект мог поддерживать слабые ссылки, тип расширения должен выполнить два условия:4694701. Включить в структуру C-объекта поле [PyObject](https://python-all.ru/3.11/c-api/structures.html#c.PyObject)\*, предназначенное для механизма слабых ссылок. Конструктор объекта должен оставлять его `NULL` (что происходит автоматически при использовании [`tp_alloc`](https://python-all.ru/3.11/c-api/typeobj.html#c.PyTypeObject.tp_alloc) по умолчанию).4712. Установить член типа [`tp_weaklistoffset`](https://python-all.ru/3.11/c-api/typeobj.html#c.PyTypeObject.tp_weaklistoffset) в смещение упомянутого поля в структуре C-объекта, чтобы интерпретатор знал, как получить доступ к этому полю и изменить его.472473Конкретно, вот как тривиальная структура объекта будет дополнена необходимым полем:474475```c476typedef struct {477    PyObject_HEAD478    PyObject *weakreflist;  /* Список слабых ссылок */479} TrivialObject;480```481482И соответствующий член в статически объявленном объекте типа:483484```c485static PyTypeObject TrivialType = {486    PyVarObject_HEAD_INIT(NULL, 0)487    /* ... остальные поля опущены для краткости ... */488    .tp_weaklistoffset = offsetof(TrivialObject, weakreflist),489};490```491492Единственное дополнительное изменение заключается в том, что `tp_dealloc` должен очистить все слабые ссылки (вызвав [`PyObject_ClearWeakRefs()`](https://python-all.ru/3.11/c-api/weakref.html#c.PyObject_ClearWeakRefs)), если поле не равно `NULL`:493494```c495static void496Trivial_dealloc(TrivialObject *self)497{498    /* Сначала очистить слабые ссылки перед вызовом любых деструкторов */499    if (self->weakreflist != NULL)500        PyObject_ClearWeakRefs((PyObject *) self);501    /* ... остальной код уничтожения опущен для краткости ... */502    Py_TYPE(self)->tp_free((PyObject *) self);503}504```505506## 3.7. Дополнительные рекомендации507508Чтобы научиться реализовывать любой конкретный метод для вашего нового типа данных, возьмите исходный код [CPython](https://python-all.ru/3.11/glossary.html#term-CPython). Перейдите в каталог `Objects`, затем выполните поиск по C-файлам на `tp_` и нужную вам функцию (например, `tp_richcompare`). Вы найдете примеры функции, которую хотите реализовать.509510Когда нужно проверить, что объект является конкретным экземпляром типа, который вы реализуете, используйте функцию [`PyObject_TypeCheck()`](https://python-all.ru/3.11/c-api/object.html#c.PyObject_TypeCheck). Пример её использования может выглядеть так:511512```c513if (!PyObject_TypeCheck(some_object, &MyType)) {514    PyErr_SetString(PyExc_TypeError, "arg #1 not a mything");515    return NULL;516}517```518519> **См. также**520>521> **Скачать исходные дистрибутивы CPython.**522>523> [https://www.python.org/downloads/source/](https://python-all.ru/3.11/extending/newtypes.html)524>525> **Проект CPython на GitHub, где разрабатывается исходный код CPython.**526>527> [https://github.com/python/cpython](https://python-all.ru/3.11/extending/newtypes.html)528