У меня есть следующий код Python:
class Meta(type):
def __call__(cls, *args, **kwargs):
obj = type.__call__(cls, *args, **kwargs)
# Only do checks for subclasses
if cls.__name__ == 'Parent':
return obj
required_attrs = ['x']
for ra in required_attrs:
if ra not in dir(obj):
fmt = 'Subclasses of Parent must define the %s attribute'
raise NotImplementedError(fmt % ra)
return obj
class Parent(metaclass=Meta):
pass
class Child(Parent):
def __init__(self):
self.x = True
Meta
используется только для того, чтобы потребовать, чтобы Child
определял определенные атрибуты. Эта структура классов должна оставаться как есть, потому что так устроен мой проект. Parent
на самом деле называется DefaultConfig
, а Child
на самом деле является определяемым пользователем классом, производным от DefaultConfig
.
Я работаю над переводом Meta
и Parent
в расширение C. Это модуль:
#include <Python.h>
#include <structmember.h>
#define ARRLEN(x) sizeof(x)/sizeof(x[0])
typedef struct {
PyObject_HEAD
} MetaObject;
typedef struct {
PyObject_HEAD
} ParentObject;
static PyObject *Meta_call(MetaObject *type, PyObject *args, PyObject *kwargs) {
PyObject *obj = PyType_GenericNew((PyTypeObject *) type, args, kwargs);
// Only do checks for subclasses of Parent
if (strcmp(obj->ob_type->tp_name, "Parent") == 0)
return obj;
// Get obj's attributes
PyObject *obj_dir = PyObject_Dir(obj);
if (obj_dir == NULL)
return NULL;
char *required_attrs[] = {"x"};
// Raise an exception of obj doesn't define all required_attrs
PyObject *attr_obj;
int has_attr;
for (int i=0; i<ARRLEN(required_attrs); i++) {
attr_obj = PyUnicode_FromString(required_attrs[i]);
has_attr = PySequence_Contains(obj_dir, attr_obj);
if (has_attr == 0) {
printf("Subclasses of Parent must define %s\n", required_attrs[i]);
// raise NotImplementedError
return NULL;
} else if (has_attr == -1) {
return NULL;
}
}
return obj;
}
static PyTypeObject MetaType = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "custom.Meta",
.tp_basicsize = sizeof(MetaObject),
.tp_itemsize = 0,
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
.tp_new = PyType_GenericNew,
.tp_call = (ternaryfunc) Meta_call,
};
static PyTypeObject ParentType = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "custom.Parent",
.tp_basicsize = sizeof(ParentObject),
.tp_itemsize = 0,
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
.tp_new = PyType_GenericNew,
};
static PyModuleDef custommodule = {
PyModuleDef_HEAD_INIT,
.m_name = "custom",
.m_size = -1,
};
PyMODINIT_FUNC PyInit_custom(void) {
PyObject *module = PyModule_Create(&custommodule);
if (module == NULL)
return NULL;
// Should Parent inherit from Meta?
ParentType.tp_base = &MetaType;
if (PyType_Ready(&MetaType) < 0)
return NULL;
Py_INCREF(&MetaType);
PyModule_AddObject(module, "Meta", (PyObject *) &MetaType);
if (PyType_Ready(&ParentType) < 0)
return NULL;
Py_INCREF(&ParentType);
PyModule_AddObject(module, "Parent", (PyObject *) &ParentType);
return module;
}
Это код Python, используемый для тестирования модуля custom
:
import custom
class Child(custom.Parent):
def __init__(self):
self.x = True
if __name__ == '__main__':
c = Child()
К сожалению, в структуре PyTypeObject
нет члена .tp_meta
, так как мне указать Meta
в качестве метакласса Parent
?
ИЗМЕНИТЬ:
Модифицированный код C:
#include <Python.h>
#include <structmember.h>
#define ARRLEN(x) sizeof(x)/sizeof(x[0])
typedef struct {
PyObject_HEAD
PyTypeObject base;
} MetaObject;
typedef struct {
PyObject_HEAD
} ParentObject;
static PyObject *Meta_call(MetaObject *type, PyObject *args, PyObject *kwargs) {
PyObject *obj = PyType_GenericNew((PyTypeObject *) type, args, kwargs);
// Only do checks for subclasses of Parent
if (strcmp(obj->ob_type->tp_name, "Parent") == 0)
return obj;
// Get obj's attributes
PyObject *obj_dir = PyObject_Dir(obj);
if (obj_dir == NULL)
return NULL;
char *required_attrs[] = {"x"};
// Raise an exception of obj doesn't define all required_attrs
PyObject *attr_obj;
int has_attr;
for (int i=0; i<ARRLEN(required_attrs); i++) {
attr_obj = PyUnicode_FromString(required_attrs[i]);
has_attr = PySequence_Contains(obj_dir, attr_obj);
if (has_attr == 0) {
printf("Subclasses of Parent must define %s\n", required_attrs[i]);
// raise NotImplementedError
return NULL;
} else if (has_attr == -1) {
return NULL;
}
}
return obj;
}
static PyTypeObject MetaType = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "custom.Meta",
.tp_basicsize = sizeof(MetaObject),
.tp_itemsize = 0,
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
.tp_new = PyType_GenericNew,
.tp_call = (ternaryfunc) Meta_call,
};
static PyTypeObject ParentType = {
PyVarObject_HEAD_INIT(&MetaType, 0)
.tp_name = "custom.Parent",
.tp_basicsize = sizeof(ParentObject),
.tp_itemsize = 0,
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
.tp_new = PyType_GenericNew,
};
static PyModuleDef custommodule = {
PyModuleDef_HEAD_INIT,
.m_name = "custom",
.m_size = -1,
};
PyMODINIT_FUNC PyInit_custom(void) {
PyObject *module = PyModule_Create(&custommodule);
if (module == NULL)
return NULL;
MetaType.tp_base = &PyType_Type;
if (PyType_Ready(&MetaType) < 0)
return NULL;
Py_INCREF(&MetaType);
PyModule_AddObject(module, "Meta", (PyObject *) &MetaType);
if (PyType_Ready(&ParentType) < 0)
return NULL;
Py_INCREF(&ParentType);
PyModule_AddObject(module, "Parent", (PyObject *) &ParentType);
return module;
}