如何从C扩展中定义Python元类?

如何从C扩展中定义Python元类?,python,cpython,metaclass,python-extensions,Python,Cpython,Metaclass,Python Extensions,在纯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

在纯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扩展中定义这个元类?

  • 定义
    PyType\u Type
  • tp\u init
  • 通过在
    PyVarObject\u HEAD\u INIT
#包括
#包括
结构foometa{
PyTypeObject头;
};
int foometa_init(foometa*cls、PyObject*args、PyObject*kwargs){
if(PyType_Type.tp_init((PyObject*)cls、args、kwargs)<0){
返回-1;
}
标准:cerr tp_alloc(类型,0);
返回obj;
}
静态PyTypeObject FoopParent\u类型={
PyVarObject\u HEAD\u INIT(&foometa\u类型,0)
“demo.fooparent”,
sizeof(PyObject),
0,
0,/*tp_dealoc*/
0,/*tp_打印*/
0,/*tp\u getattr*/
0,/*tp_setattr*/
0,/*tp_保留*/
0,/*tp_repr*/
0,/*tp\U作为\U编号*/
0,/*tp\u作为\u序列*/
0,/*tp_作为映射*/
0,/*tp_散列*/
0,/*tp\u调用*/
0,/*tp_str*/
0,/*tp_getattro*/
0,/*tp_setattro*/
0,/*tp作为缓冲区*/
Py_TPFLAGS|Py_TPFLAGS|BASETYPE,/*tp_标志*/
0,/*tp_文件*/
0,/*tp\u导线测量*/
0,/*tp_清除*/
0,/*tp_*/
0,/*tp_弱偏移*/
0,/*tp_iter*/
0,/*tp_iternext*/
0,/*tp_方法*/
0,/*tp_成员*/
0,/*tp\u getset*/
0,/*tp_基*/
0,/*tp_dict*/
0,/*tp\u descr\u get*/
0,/*tp\u descr\u集*/
0,/*tp\u偏移量*/
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){
返回-1;
}
if(PyType\u Ready(&foopparent\u type)<0){
返回-1;
}
Py_增量(&foometa_类型);
if(PyModule_AddObject)(m,“foometa”,
(PyObject*)和Foomata_类型)<0)
返回-1;
Py_增量(&fooparent_类型);
if(PyModule_AddObject)(m,“fooparent”,
(PyObject*)和fooparent_类型)<0)
返回-1;
返回0;
}
静态PyModuleDef demomodule={
PyModuleDef_HEAD_INIT,
“演示”,
“示例模块”,
-1,
空,空,空,空,空,空
};
PyMODINIT_FUNC
PyInit_demo(){
PyObject*m=PyModule\u创建(&demomodule);
如果(m==nullptr),则返回nullptr;
if(demo_init(m)<0)返回nullptr;
返回m;
}

完整示例runnable at

在这段代码中有一个bug,如果你从fooparent子类化两次,你将使assert
Modules/gcmodule失败。c:714:handle_weakrefs:Assertion“wr->wr_object==op”失败
bug已经修复,我认为。foometa结构没有足够的字段,有趣的是,因此它的定义可能是错误的。
tp_init
是与
\uuu init\uuu
等价的C,而不是
\uu new\uu
<代码>\uuuuuuuuuuuuuuuuuuuuuuu逻辑应进入
tp\u new
#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;
}