LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
查看: 255|回复: 0

子类化其他类型

[复制链接]
发表于 2024-1-18 18:35:23 | 显示全部楼层 |阅读模式

子类化其他类型
创建派生自现有类型的新类型是有可能的。 最容易的做法是从内置类型继承,因为扩展可以方便地使用它所需要的 PyTypeObject。 在不同扩展模块之间共享这些 PyTypeObject 结构体则是困难的。

在本例中我们将创建一个继承自内置 list 类型的 SubList 类型。 这个新类型将完全兼容常规列表,但将拥有一个额外的 increment() 方法用于递增内部计数器的值:

>>>
import sublist
s = sublist.SubList(range(3))
s.extend(s)
print(len(s))
6
print(s.increment())
1
print(s.increment())
2
#define PY_SSIZE_T_CLEAN
#include <;Python.h>

typedef struct {
    PyListObject list;
    int state;
} SubListObject;

static PyObject *
SubList_increment(SubListObject *self, PyObject *unused)
{
    self->state++;
    return PyLong_FromLong(self->state);
}

static PyMethodDef SubList_methods[] = {
    {"increment", (PyCFunction) SubList_increment, METH_NOARGS,
     PyDoc_STR("increment state counter")},
    {NULL},
};

static int
SubList_init(SubListObject *self, PyObject *args, PyObject *kwds)
{
    if (PyList_Type.tp_init((PyObject *) self, args, kwds) < 0)
        return -1;
    self->state = 0;
    return 0;
}

static PyTypeObject SubListType = {
    PyVarObject_HEAD_INIT(NULL, 0)
    .tp_name = "sublist.SubList",
    .tp_doc = PyDoc_STR("SubList objects"),
    .tp_basicsize = sizeof(SubListObject),
    .tp_itemsize = 0,
    .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
    .tp_init = (initproc) SubList_init,
    .tp_methods = SubList_methods,
};

static PyModuleDef sublistmodule = {
    PyModuleDef_HEAD_INIT,
    .m_name = "sublist",
    .m_doc = "Example module that creates an extension type.",
    .m_size = -1,
};

PyMODINIT_FUNC
PyInit_sublist(void)
{
    PyObject *m;
    SubListType.tp_base = &PyList_Type;
    if (PyType_Ready(&SubListType) < 0)
        return NULL;

    m = PyModule_Create(&sublistmodule);
    if (m == NULL)
        return NULL;

    Py_INCREF(&SubListType);
    if (PyModule_AddObject(m, "SubList", (PyObject *) &SubListType) < 0) {
        Py_DECREF(&SubListType);
        Py_DECREF(m);
        return NULL;
    }

    return m;
}
如你所见,此源代码与之前几节中的 Custom 示例非常相似。 我们将逐一分析它们之间的主要区别。

typedef struct {
    PyListObject list;
    int state;
} SubListObject;
派生类型对象的主要差异在于基类型的对象结构体必须是第一个值。 基类型将已经在其结构体的开头包括了 PyObject_HEAD()。

当一个 Python 对象是 SubList 的实例时,它的 PyObject * 指针可以被安全地强制转换为 PyListObject * 和 SubListObject *:

static int
SubList_init(SubListObject *self, PyObject *args, PyObject *kwds)
{
    if (PyList_Type.tp_init((PyObject *) self, args, kwds) < 0)
        return -1;
    self->state = 0;
    return 0;
}
我们可以在上面看到如何将调用传递到基类型的 __init__() 方法。

这个模式在编写具有自定义 tp_new 和 tp_dealloc 成员的类型时很重要。 tp_new 处理句柄不应为具有 tp_alloc 的对象实际分配内存,而是让基类通过调用自己的 tp_new 来处理它。

PyTypeObject 支持用 tp_base 指定类型的实体基类。 由于跨平台编译器问题,你无法使用对 PyList_Type 的引用来直接填充该字段;它应当随后在模块初始化函数中完成:

PyMODINIT_FUNC
PyInit_sublist(void)
{
    PyObject* m;
    SubListType.tp_base = &PyList_Type;
    if (PyType_Ready(&SubListType) < 0)
        return NULL;

    m = PyModule_Create(&sublistmodule);
    if (m == NULL)
        return NULL;

    Py_INCREF(&SubListType);
    if (PyModule_AddObject(m, "SubList", (PyObject *) &SubListType) < 0) {
        Py_DECREF(&SubListType);
        Py_DECREF(m);
        return NULL;
    }

    return m;
}
在调用 PyType_Ready() 之前,类型结构体必须已经填充 tp_base 槽位。 当我们从现有类型派生时,它不需要将 tp_alloc 槽位填充为 PyType_GenericNew() -- 来自基类型的分配函数将会被继承。

在那之后,调用 PyType_Ready() 并将类型对象添加到模块中的过程与基本的 Custom 示例是一样的。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表