Как добиться полиморфизма в Python C API?

Я пишу functools.partial альтернатива объекта, которая накапливает аргументы, пока их число не станет достаточным для совершения вызова.

Я использую C API, и у меня есть tp_call реализация, которая при вызове возвращает измененную версию self или PyObject*,

Сначала я следовал руководству " Определение новых типов", а потом понял, что просто не могу возвращать разные типы (PyObject * а также MyObject*) от tp_call реализация. Тогда я пытался не использовать struct с MyObject* определение и использование PyObject_SetAttrString в tp_init вместо этого, как мы делаем это в Python. Но в этом случае я получил AttributeError потому что вы не можете установить произвольные атрибуты на object экземпляры в Python.

Что мне нужно здесь, чтобы сделать мой tp_call реализация полиморфная, и сделать ее способной вернуть либо MyObject который является подклассом PyObject, или же PyObject набери сам.

Какой разумный способ сделать это?

ОБНОВЛЕНИЕ № 0

Вот код:

static PyObject *Curry_call(Curry *self, PyObject *args, 
                            PyObject *kwargs) {
    PyObject * old_args = self->args;
    self->args = PySequence_Concat(self->args, args);
    Py_DECREF(old_args);
    if (self->kwargs == NULL && kwargs != NULL) {
        self->kwargs = kwargs;
        Py_INCREF(self->kwargs);
    } else if (self->kwargs != NULL && kwargs != NULL) {
        PyDict_Merge(self->kwargs, kwargs, 1);
    }

    if ((PyObject_Size(self->args) +
         (self->kwargs != NULL ? PyObject_Size(self->kwargs) : 0)) >=
        self->num_args) {
        return PyObject_Call(self->fn, self->args, self->kwargs);
    } else {
        return (PyObject *)self;
    }
}

ОБНОВЛЕНИЕ № 1

Почему я изначально отказался от этой реализации - потому что я получаю segfault с ним при последующих вызовах частичного объекта. Я думал, что это происходит из-за кастинга Curry * в PyObject* проблемы. Но теперь я исправил ошибку, добавив Py_INCREF(self); до возвращения (PyObject *)self;, Очень странно для меня. Должен ли я действительно INCREF self, если я возвращаю его по правилам владения C API?

2 ответа

Если вы определили MyObject введите правильно, вы должны иметь возможность просто разыграть MyObject * к PyObject * и верни это. Первый член MyObject это PyObjectи C позволяет вам привести указатель на структуру к указателю на первый член структуры и наоборот. Я полагаю, что эта функция существует специально для таких вещей.

Я не знаю весь ваш код, но пока MyObject это PyObject (совместимый, то есть имеет те же поля "заголовка", убедитесь, что у вас есть поле длины), CPython предназначен для того, чтобы просто принимать MyObject в качестве PyObject; просто приведите указатель к PyObject, прежде чем возвращать его.

Как вы можете видеть здесь, это одна из вещей, которая удобна при использовании C++: у вас могут быть подклассы с безопасностью типов, и вам не нужно беспокоиться о том, что кто-то просто скопирует более половины экземпляра вашего подкласса, например,

РЕДАКТИРОВАТЬ: потому что его спросили "не это небезопасно": да. Это. Но это так же небезопасно, как обработка типов в пользовательском коде; CPython позволяет вам сделать это, потому что он хранит и проверяет PyTypeObject *ob_type член PyObject структура содержится. Это примерно так же безопасно, как, например, проверка типа C++ во время выполнения - но она реализована разработчиками python, а не разработчиками GCC/clang/MSVC/icc/....

Другие вопросы по тегам