问题描述
我有以下 python 代码:
I have the following python code:
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
is used only to require that Child
defines certain attributes. This class structure must remain as is because this is how my project is structured. Parent
is actually called DefaultConfig
and Child
is actually a user-defined class derived from DefaultConfig
.
我正在将 Meta
和 Parent
翻译成 C 扩展.这是模块:
I'm working on translating Meta
and Parent
into a C extension. This is the module:
#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;
}
这是用于测试模块custom
的python代码:
This is the python code used to test module custom
:
import custom
class Child(custom.Parent):
def __init__(self):
self.x = True
if __name__ == '__main__':
c = Child()
不幸的是,PyTypeObject
结构体中没有.tp_meta
成员,那么如何指定Meta
作为 的元类父
?
Unfortunately, there is no .tp_meta
member in the PyTypeObject
struct, so how do I specify Meta
as the metaclass of 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;
}
推荐答案
元类不过是一种类型,它被用作类型(ob_type
!) 的类(类型)...(清楚,不是吗)... ParentType
不是从 MetaType继承的code> 但它是 `MetaType 的一个实例.
The metaclass is nothing but a type that is used as the type (ob_type
!) of the class (type)... (clear, isn't it)... ParentType
does not inherit from MetaType
but is an instance of `MetaType.
因此,&MetaType
应该去的地方是ParentType.ob_type
:
PyModule_AddObject(module, "Meta", (PyObject *) &MetaType);
ParentType.ob_type = &MetaType;
if (PyType_Ready(&ParentType) < 0)
PyType_Ready
检查 ob_type
字段 - 如果它是 NULL
,则取 ob_type
的 >.tp_base
;但是如果 ob_type
已经设置,它会保持原样.
PyType_Ready
checks the ob_type
field - if it is NULL
, it takes the ob_type
of the .tp_base
; but if ob_type
is set already, it is left as is.
其实你可以在ParentType
初始化器中设置:
Actually you can set it in the ParentType
initializer:
PyVarObject_HEAD_INIT(&MetaType, 0)
第一个参数进入 ob_type
字段.
The first argument goes to the ob_type
field.
这篇关于C 语言中的 Python 扩展 - 元类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!