gpt4 book ai didi

C 中的 Python 扩展 - 元类

转载 作者:行者123 更新时间:2023-11-30 16:26:46 26 4
gpt4 key购买 nike

我有以下 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 派生的用户定义类。

我正在将 MetaParent 转换为 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;
}

这是用于测试模块custom的Python代码:

import custom

class Child(custom.Parent):
def __init__(self):
self.x = True

if __name__ == '__main__':
c = Child()

不幸的是,PyTypeObject结构中没有.tp_meta成员,那么如何指定Meta作为的元类父级

<小时/>

编辑:

修改后的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但是是`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已设置,保持原样。

其实可以在 ParentType 中设置初始化器:

PyVarObject_HEAD_INIT(&MetaType, 0)

第一个参数指向 ob_type字段。

关于C 中的 Python 扩展 - 元类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52957192/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com