Python C API: как проверить, является ли объект экземпляром типа - PullRequest
1 голос
/ 20 мая 2019

Я хочу проверить, является ли объект экземпляром определенного класса.В Python я могу сделать это с instanceof.В C / C ++ я нашел функцию с именем PyObject_IsInstance .Но, похоже, он не работает, как isinstance.

Подробно (также описанный ниже в качестве примеров кода):

  1. В C ++ я определил свой пользовательский тип My.Определение типа - MyType, а определение объекта - MyObject.
  2. Добавьте MyType в экспортируемый модуль с именем My.
  3. В Python создайте новый экземпляр my = My() и isinstance(my, My) возвращает True.
  4. В то время как в C ++ мы используем PyObject_IsInstance(my, (PyObject*)&MyType) для проверки my, и это возвращает 0, что означает, что my не является экземпляромкласс, определенный как MyType.

Полный код C ++:

#define PY_SSIZE_T_CLEAN
#include <python3.6/Python.h>
#include <python3.6/structmember.h>
#include <stddef.h>

typedef struct {
    PyObject_HEAD
    int num;
} MyObject;

static PyTypeObject MyType = []{
    PyTypeObject ret = {
        PyVarObject_HEAD_INIT(NULL, 0)
    };
    ret.tp_name = "cpp.My";
    ret.tp_doc = NULL;
    ret.tp_basicsize = sizeof(MyObject);
    ret.tp_itemsize = 0;
    ret.tp_flags = Py_TPFLAGS_DEFAULT;
    ret.tp_new = PyType_GenericNew;
    return ret;
}();

// check if obj is an instance of MyType
static PyObject *Py_fn_checkMy(PyObject *obj) {
    if (PyObject_IsInstance(obj, (PyObject *)&MyType)) Py_RETURN_TRUE;
    else Py_RETURN_FALSE;
}

static PyMethodDef modmethodsdef[] = {
    { "checkMy", (PyCFunction)Py_fn_checkMy, METH_VARARGS, NULL },
    { NULL }
};

static PyModuleDef moddef = []{
    PyModuleDef ret = {
        PyModuleDef_HEAD_INIT
    };
    ret.m_name = "cpp";
    ret.m_doc = NULL;
    ret.m_size = -1;
    return ret;
}();

PyMODINIT_FUNC
PyInit_cpp(void)
{
    PyObject *mod;
    if (PyType_Ready(&MyType) < 0)
        return NULL;
    mod = PyModule_Create(&moddef);
    if (mod == NULL)
        return NULL;
    Py_INCREF(&MyType);
    PyModule_AddObject(mod, "My", (PyObject *)&MyType);
    PyModule_AddFunctions(mod, modmethodsdef);
    return mod;
}

Скомпилируйте это в cpp.so и протестируйте в Python:

>>> import cpp
>>> isinstance(cpp.My(), cpp.My)
True
>>> cpp.checkMy(cpp.My())
False

1 Ответ

1 голос
/ 20 мая 2019

METH_VARARGS

Это типичное соглашение о вызовах, где методы имеют тип PyCFunction.Функция ожидает два значения PyObject*.Первый - это собственный объект для методов;для функций модуля это объект модуля.Второй параметр (часто называемый args) является объектом кортежа, представляющим все аргументы.Этот параметр обычно обрабатывается с использованием PyArg_ParseTuple() или PyArg_UnpackTuple().

Сигнатура функции Py_fn_checkMy не соответствует этому.Это должно принять два аргумента.Первый - это модуль, и это то, что вы проверяете по MyType.Второй аргумент (который вы на самом деле не принимаете) - это кортеж, содержащий объект, который вы передали.Вы должны извлечь аргумент из кортежа и проверить его тип.

Возможно, вам лучше было бы использовать METH_O для указания одного аргумента вместо извлечения аргументов из кортежа:

static PyObject *Py_fn_checkMy(PyObject *self, PyObject *obj) {
    if (PyObject_IsInstance(obj, (PyObject *)&MyType)) Py_RETURN_TRUE;
    else Py_RETURN_FALSE;
}


static PyMethodDef modmethodsdef[] = {
    { "checkMy", (PyCFunction)Py_fn_checkMy, METH_O, NULL },
    { NULL }
};
...