У меня есть следующий код 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
struct, так как мне указать Meta
в качестве метакласса Parent
?
EDIT :
Модифицированный код 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;
}