【python源码】——采用c为python写方法拓展(二)

关键词:python3,c,拓展
转载自:https://docs.python.org/zh-cn/3.6/extending/newtypes_tutorial.html
系列链接:

  1. 【python源码】——采用c为python写方法拓展

1. 环境

  • python3.6
  • gcc7.5

2. code

#include <Python.h>
#include "structmember.h"

// 定义一个新的object
typedef struct {
    PyObject_HEAD
    PyObject *first; /* first name */
    PyObject *last;  /* last name */
    int number;
} CustomObject;

// 清除变量与内存,没有显示的调用
static void
Custom_dealloc(CustomObject *self)
{
    Py_XDECREF(self->first);
    Py_XDECREF(self->last);
    Py_TYPE(self)->tp_free((PyObject *) self);
}

// 对应python中 __new__ 创建对象,没有显示调用
static PyObject *
Custom_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
    CustomObject *self;
    self = (CustomObject *) type->tp_alloc(type, 0);
    if (self != NULL) {
        self->first = PyUnicode_FromString("");
        if (self->first == NULL) {
            Py_DECREF(self);
            return NULL;
        }
        self->last = PyUnicode_FromString("");
        if (self->last == NULL) {
            Py_DECREF(self);
            return NULL;
        }
        self->number = 123;
    }
    return (PyObject *) self;
}
// 对应到 python 中 __init__ 初始化对象,没有在程序中显示的调用
static int
Custom_init(CustomObject *self, PyObject *args, PyObject *kwds)
{
    static char *kwlist[] = {"first", "last", "number", NULL};
    PyObject *first = NULL, *last = NULL, *tmp;

    if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOi", kwlist,
                                     &first, &last,
                                     &self->number))
        return -1;

    if (first) {
        tmp = self->first;
        Py_INCREF(first);
        self->first = first;
        Py_XDECREF(tmp);
    }
    if (last) {
        tmp = self->last;
        Py_INCREF(last);
        self->last = last;
        Py_XDECREF(tmp);
    }
    return 0;       // 返回0/1
}

// 定义对象的一些属性
static PyMemberDef Custom_members[] = {
    {"first", T_OBJECT_EX, offsetof(CustomObject, first), 0,
     "first name"},
    {"last", T_OBJECT_EX, offsetof(CustomObject, last), 0,
     "last name"},
    {"number", T_INT, offsetof(CustomObject, number), 0,
     "custom number"},
    {NULL}  /* Sentinel */
};

// 定义模块的函数, 在python中采用module.name()进行调用
static PyObject *
Custom_name(CustomObject *self, PyObject *Py_UNUSED(ignored))
{
    if (self->first == NULL) {
        PyErr_SetString(PyExc_AttributeError, "first");
        return NULL;
    }
    if (self->last == NULL) {
        PyErr_SetString(PyExc_AttributeError, "last");
        return NULL;
    }
    return PyUnicode_FromFormat("%S %S", self->first, self->last);
}

// 声明的结构体
static PyMethodDef Custom_methods[] = {
    {"name", (PyCFunction) Custom_name, METH_NOARGS,
     "Return the name, combining the first and last name"
    },
    {NULL}  /* Sentinel */
};

// 定义类类型
static PyTypeObject CustomType = {
    PyVarObject_HEAD_INIT(NULL, 0)
    .tp_name = "custom2.Custom",
    .tp_doc = "Custom objects",
    .tp_basicsize = sizeof(CustomObject),
    .tp_itemsize = 0,
    .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
    .tp_new = Custom_new,
    .tp_init = (initproc) Custom_init,
    .tp_dealloc = (destructor) Custom_dealloc,
    .tp_members = Custom_members,
    .tp_methods = Custom_methods,
};

// 定义一个新module 用的结构体
static PyModuleDef custommodule = {
    PyModuleDef_HEAD_INIT,
    .m_name = "custom2",
    .m_doc = "Example module that creates an extension type.",
    .m_size = -1,
};

PyMODINIT_FUNC
PyInit_custom2(void)
{
    PyObject *m;
    if (PyType_Ready(&CustomType) < 0)      // 完善 CustomType 的 tp_alloc
        return NULL;

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

    Py_INCREF(&CustomType);
    PyModule_AddObject(m, "Custom", (PyObject *) &CustomType);
    return m;
}

/*
1. compile
gcc -shared -fPIC -I/usr/include/python3.6 custom2.c -o custom2.so
- custom2.so,名字需要和模块的名字一致

*/

具体解释,请看代码中的注释

3. python 测试

code

import custom2

# 问题:
# 1. 什么时候调用new方法
# 2. 什么时候调用init方法
# 3. new和init的关系
class my(custom2.Custom):
    def __new__(cls, *args, **kwargs):
        print("__new__", cls, args, kwargs)
        instance = super().__new__(cls, args, kwargs)         # 调用父类的__new__方法,就会调用Custom2.c中new方法
        print("values:", instance.first, instance.last, instance.number)
        return instance

    def __init__(self, *args, **kwargs):
        print("__init__", self, args, kwargs)
        super(my, self).__init__(*args, **kwargs)           # 调用父类的__init__方法,就是调用custom2.c中的init方法
        print("self.values:", self.first, self.last, self.number)

b = my("boy", "girl")
print(b.name())

c = my(first="boy", last="girl")
print(c.name())

输出:

__new__ <class '__main__.my'> ('boy', 'girl') {}
values:   123
__init__ <__main__.my object at 0x7f97da34ad18> ('boy', 'girl') {}
self.values: boy girl 123
boy girl
__new__ <class '__main__.my'> () {'first': 'boy', 'last': 'girl'}
values:   123
__init__ <__main__.my object at 0x7f97da34ae08> () {'first': 'boy', 'last': 'girl'}
self.values: boy girl 123
boy girl

4. other

  1. new__和__init 的关系:http://c.biancheng.net/view/5484.html
  2. CustomObject 和 CustomType 是如何对应起来的呢?

在初始化的时候,有

CustomObject *self;
self = (CustomObject *) type->tp_alloc(type, 0);

这里是先申请内存type->tp_alloc(type, 0),再强制转换成(CustomObject *),而申请内存的时候,只需要知道内存的大小,所以type->tp_alloc(type, 0)相当于new(type->tp_basicsize ),而tp_basicsize = sizeof(CustomObject)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值