python - 如何从 C 扩展定义 Python 元类?
问题描述
在纯 Python 中,定义和使用元类相对简单。
class Meta(type):
def __new__(cls, name, bases, dict):
x = super().__new__(cls, name, bases, dict)
print("I'm called on class construction time!")
return x
class A(metaclass=Meta):
pass
class B(A):
pass
你如何从 Python C 扩展定义这个元类?
解决方案
- 定义元类的子类 (
tp_base
)PyType_Type
- 将
__new__
逻辑放入tp_init
- 通过在其他类中引用元类来使用它
PyVarObject_HEAD_INIT
#include <Python.h>
#include <iostream>
struct foometa {
PyTypeObject head;
};
int foometa_init(foometa *cls, PyObject *args, PyObject *kwargs) {
if (PyType_Type.tp_init((PyObject*)cls, args, kwargs) < 0) {
return -1;
}
std::cerr << "I'm called on class construction time!\n";
return 0;
}
#define DEFERRED_ADDRESS(ADDR) nullptr
static PyTypeObject foometa_type = {
PyVarObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type), 0)
"demo.foometa",
0,
0,
0, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_reserved */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
0, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
0, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
DEFERRED_ADDRESS(&PyType_Type), /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
(initproc)foometa_init, /* tp_init */
0, /* tp_alloc */
0 /* tp_new */
};
PyObject *fooparent_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
PyObject* obj = type->tp_alloc(type, 0);
return obj;
}
static PyTypeObject fooparent_type = {
PyVarObject_HEAD_INIT(&foometa_type, 0)
"demo.fooparent",
sizeof(PyObject),
0,
0, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_reserved */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
0, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
0, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
fooparent_new /* tp_new */
};
int
demo_init(PyObject *m) {
foometa_type.tp_base = &PyType_Type;
if (PyType_Ready(&foometa_type) < 0) {
return -1;
}
if (PyType_Ready(&fooparent_type) < 0) {
return -1;
}
Py_INCREF(&foometa_type);
if (PyModule_AddObject(m, "foometa",
(PyObject *) &foometa_type) < 0)
return -1;
Py_INCREF(&fooparent_type);
if (PyModule_AddObject(m, "fooparent",
(PyObject *) &fooparent_type) < 0)
return -1;
return 0;
}
static PyModuleDef demomodule = {
PyModuleDef_HEAD_INIT,
"demo",
"Example module",
-1,
NULL, NULL, NULL, NULL, NULL
};
PyMODINIT_FUNC
PyInit_demo() {
PyObject* m = PyModule_Create(&demomodule);
if (m == nullptr) return nullptr;
if (demo_init(m) < 0) return nullptr;
return m;
}
推荐阅读
- php - 在 null 上调用成员函数 sendEmail()
- flask - 如何将此 SQL 查询计数月份和按月分组转换为 sqlalchemy?
- css - 用于模态弹出窗口的全角按钮的 CSS
- angularjs - 通过角度形式从 mongo 搜索布尔类型字段
- javascript - ./src/index.js 第 36 行:期望一个赋值或函数调用,但看到一个表达式 no-unused-expressions
- visual-studio-code - Vscode+arduino:在哪里可以找到“arduino.json”解析器?
- python-3.x - 当我尝试将文件打开到 tkinter 文本小部件中时,但收到错误“'str' object has no attribute 'tk'”
- javascript - 如何在nodejs中将缓冲区格式化为JSON?
- input - Dialogflow:如何通过代码创建具有特定上下文输入的意图?
- javascript - 如果它有一个跨度作为子级,则将类添加到父 div