python c语言实现_使用C语言为python编写动态模块(3)--在C中实现python中的类

楔子

这次我们来介绍python中的类型在C中是如何实现的,我们在C中创建python的int对象,可以使用PyLong_FromLong、创建python的list对象可以使用PyList_New,那么如何在C中构建一个python中的类呢?

对于构建一个类,我们肯定需要以下步骤:

创建一个类扩展

添加类的参数

添加类的方法

添加类的属性,比如可以设置、获取属性

添加类的继承

解决类的循环引用导致的内存泄露问题和自定义垃圾回收

前面几个步骤是必须的,但是容易把最后一个问题给忽略掉。我们在python中编写模块的话,那么内存方面的问题不需要我们来考虑,但是当编写扩展模块的时候,我们是在C中编写的,因此内存方面都需要我们自己来管理。

python是通过引用计数来决定一个对象是否应该被回收,这是最简单、最原始、但也是最方便的方法。尽管它有很多的不足,python还是采用了这种方法,因为它非常简单、直观。但是也正如我们说的,它存在着不足,其不足就在于无法解决循环引用的问题,所以python中的gc就是来干这个事情的,通过分代技术根据对象的生命周期划分为三个链表,然后通过三色标记模型来找出那些具有循环引用的对象,改变它们的引用计数。所以在python中一个对象是否要被回收,最终还是取决于它的引用计数是否为0。

所以如果让你简述一下python的垃圾回收机制,你就可以回答:引用计数为主,分代技术为辅。

因此我们在做类的扩展的时候,这些问题就必须由我们来考虑了。

编写扩展类前奏曲

我们之前编写了扩展函数,我们说首先要创建一个模块,这里也是一样的。因为类也要在模块里面,编写函数是有套路的,编写类也是一样。我们还是先看看大致的流程,具体细节会在编写扩展类的时候介绍。

类名、构造函数、析构函数

PyTypeObject,我们说类也是一个对象,它们都是一个PyTypeObject实例

PyType_Ready,初始化

PyModule_AddObject,将扩展类添加进模块中

PyTypeObject实例化一个类之后,我们是需要设置一些属性的,假设叫my_class吧,注意这个my_class只是C中的一个变量名,它和python中的类没有什么关系。那么我们需要设置如下属性:

my_class.ob_base = { PyObject_HEAD_INIT(&PyBaseObject_Type) 0 },这个写法比较固定,表示设置基类以及头部信息

my_class.tp_name = "MyClass";,类名,但是注意:这个名字不对应类,只是用来显示。比如你在python中,A = type("B", (object, ), {}),此时创建的类的名字就叫做B,对应这里的tp_name,但是使用的时候是通过A来使用的。不过从开发的角度来讲,我们认为名字应该是保持一致的。这个是必须要设置的,一个类要有名字。

my_class.tp_basicsize = sizeof(MyClass),一个类要有大小,这里的MyClass则需要单独定义了

my_class.tp_itemsize = 0;,元素的大小,这里是0,表示固定大小,因为我们的类是固定的

Py_TPFLAGS_HEAPTYPE:表示对象自己在堆中分配空间。Py_TPFLAGS_BASEYPE:是否可以被继承

从零开始创建一个类

#include "Python.h"

//懵逼的话,先看下面的代码

typedef struct{

PyObject_HEAD //头部信息

}MyClass;

//这里我们实现python中的__new__方法,这个__new__方法接收哪些参数来着

//一个类本身,以及__init__中的参数,我们一般会这样写def __new__(cls, *args, **kwargs):

//所以这里的第一个参数就不再是PyObject *了,而是PyTypeObject *

static PyObject *

MyClass_new(PyTypeObject *cls, PyObject *args, PyObject *kw)

{

//我们说python中的__new__方法默认都干了哪些事来着

//为创建的实例对象开辟一份空间,然后会将这份空间的指针返回回去交给self

//当然交给__init__的还有其它的参数,这些参数是__init__需要使用的,__new__方法不需要关心

//但是毕竟要先经过__new__方法,所以__new__方法中要有参数位能够接收

//最终__new__会将自身返回的self连同其它参数组合起来一块交给__init__

//所以__init__中self我们不需要关心,我们只需要传递self后面的参数即可,因为在__new__会自动传递self

//另外多提一嘴:我们使用实例对象调用方法的时候,会自动传递self,你有没有想过它为什么会自动传递呢?

//其实这个在底层是使用了描述符,至于底层是怎么实现的,我们有机会的话,会单独开一篇博客来分析

//所以我们这里要为self分配一个空间,self也是一个指针,但是它已经有了明确的类型,所以我们需要转化一下

//当然这里不叫self也是可以的,只是我们按照官方的约定,不会引起歧义

//分配空间是通过调用PyTypeObject的tp_alloc方法,传入一个PyTypeObject *,以及大小,这里是固定的所以是0

MyClass *self = (MyClass *)cls -> tp_alloc(cls, 0); //此时就由python管理了

//记得返回self,转成PyObject *,当然我们这里的__new__方法的默认实现,你也可以做一些其它的事情来控制一下类的实例化行为

return (PyObject *)self;

}

//构造函数接收三个PyObject *

static int

MyClass_init(PyObject *self, PyObject *args, PyObject *kw)

{

//假设这个构造函数接收三个参数:name,age,gender

char *name;

int age;

char *gender;

char *keys[] = {"name", "age", "gender", NULL};

if (!PyArg_ParseTupleAndKeywords(args, kw, "sis", keys, &name, &age, &gender)){

//结果为0返回成功,结果为-1返回失败,这里失败了不能返回NULL,而是返回-1,__init__比较特殊

return -1;

}

//至于如何设置到self当中,我们后面演示,这里先打印一下

printf("name = %s, age = %d, gender = %s\n", name, age, gender);

//结果为0返回成功,结果为-1返回失败

return 0;

}

void

MyClass_del(PyObject *self)

{

//打印一句话吧

printf("%s\n", "call __del__");

//拿到类型,调用tp_free释放

Py_TYPE(self) -> tp_free(self);

}

static PyModuleDef HANSER = {

PyModuleDef_HEAD_INIT, //头部信息

"hanser", //模块名

"this is a module named hanser", //模块注释

-1, //模块空间

0, //这里是PyMethodDef数组的首个元素的地址,但是我们这里没有PyMethodDef,所以就是0

NULL,

NULL,

NULL,

NULL

};

PyMODINIT_FUNC

PyInit_hanser(void)

{

//创建类的这些过程,我们也可以单独写在一个函数中,我们这里第一次演示就直接写在模块初始化函数里面了

//实例化一个PyTypeObject,但是这里面的属性非常多,我们通过直接赋值的方式需要写一大堆

//所以先定义,然后设置指定的属性

static PyTypeObject my_class;

//我们知道PyTypeObject结构体的第一个参数就是PyVarObject ob_base;,需要引用计数(初始为1),类型&PyType_Type,ob_size(不可变,写上0即可)

PyVarObject ob_base = {1, &PyType_Type, 0};

my_class.ob_base = ob_base; //类的公共信息

my_class.tp_name = "MyClass"; //类名

//类的大小,这个MyClass就是我们上面定义的结构体的名字,也是在python中调用类所使用的名字

//假设上面定义的是MyClass1,那么在python中你就需要使用MyClass1来实例化,但是使用type查看的时候显示的MyClass,因为类名叫MyClass

//因此在开发中两个名字要保持一致

my_class.tp_basicsize = sizeof(MyClass);

my_class.tp_itemsize = 0; //固定大小

//我们说在python中创建一个类的实例,会调用__new__方法,所以我们也需要手动实现

my_class.tp_new = MyClass_new;

//构造函数__init__

my_class.tp_init = MyClass_init;

//析构函数

my_class.tp_dealloc = MyClass_del;

//初始化类,调用PyType_Ready。

//而且python内部的类在创建完成之后也会调用这个方法进行初始化,它会对创建类进行一些属性的设置

//记得传入指针进去

if (PyType_Ready(&my_class) < 0){

//如果结果小于0,说明设置失败

return NULL;

}

//这个是我们自己创建的类,所以需要手动增加引用计数

Py_XINCREF(&my_class);

//加入到模块中,这个不需要在创建PyModuleDef的时候指定,而是可以单独添加

//我们需要先把模块创建出来

PyObject *m = PyModule_Create(&HANSER);

//方式是:传入根据PyModuleDef *创建对象,也就是我们的模块、类名(这个类名要和我们上面设置的tp_name保持一致)、以及由PyTypeObject *转化得到的PyObject *

//另外多提一嘴,这里的m、和my_class以及上面HANSER都只是C中变量,具体的模块名和类名是hanser和MyClass

PyModule_AddObject(m, "MyClass", (PyObject *)&my_class);

return m;

}

import hanser

# 然后实例化一个类

try:

# 我们说这个类的构造函数中接收三个参数,尽管我们没有设置,但是该传还是要传的

self = hanser.MyClass()

except Exception as e:

print(e)

# 尽管实例化失败,但是这个对象在__new__方法中被创建了

# 所以依旧会调用__del__

"""

call __del__

Required argument 'name' (pos 1) not found

"""

# 传递参数,但是没有设置到self里面去

self = hanser.MyClass("mashiro", 16, "female")

# 打印

"""

name = mashiro, age = 16, gender = female

"""

# 调用析构函数

del self

"""

call __del__

"""

此刻我们就实现了在C中定义一个类,不过可能有人对这个PyTypeObject内部的成员不是很熟悉,我们再单独拿出来分析一下,并且把我们上面的例子再改一下。

PyTypeObject的内部成员

//我们之前是先实例化一个PyTypeObject对象

//然后再通过.的方式设置指定的成员,因为我们说直接赋值的话需要写很多东西

//我们看貌似是有些多,但是有时候我们也会直接这样实例化

//下面我们来介绍一下内部成员都代表什么含义

typedef struct _typeobject {

//头部信息,PyVarObject ob_base; 里面包含了引用计数、类型、ob_size

//关于设置,python提供了一个宏,PyVarObject_HEAD_INIT(type, size)

//传入类型和大小可以直接创建,至于引用计数则默认为1

PyObject_VAR_HEAD

//创建之后的类名

const char *tp_name; /* For printing, in format "." */

//大小,用于申请空间的,注意了,这里是两个成员

Py_ssize_t tp_basicsize, tp_itemsize; /* For allocation */

/* Methods to implement standard operations */

//析构方法__del__,当删除实例对象时会调用这个操作

//typedef void (*destructor)(PyObject *); 函数接收一个PyObject *,没有返回值

destructor tp_dealloc;

//打印其实例对象是调用的函数

//typedef int (*printfunc)(PyObject *, FILE *, int); 函数接收一个PyObject *、FILE *和int

printfunc tp_print;

//获取属性,内部的__getattr__方法

//typedef PyObject *(*getattrfunc)(PyObject *, char *);

getattrfunc tp_getattr;

//设置属性,内部的__setattr__方法

//typedef int (*setattrfunc)(PyObject *, char *, PyObject *);

setattrfunc tp_setattr;

//在python3.5之后才产生的,这个不需要关注。

//并且在其它类的注释中,这个写的都是tp_reserved

PyAsyncMethods *tp_as_async; /* formerly known as tp_compare (Python 2)

or tp_reserved (Python 3) */

//内部的__repr__方法

//typedef PyObject *(*reprfunc)(PyObject *);

reprfunc tp_repr;

//一个对象作为数值所有拥有的方法

PyNumberMethods *tp_as_number;

//一个对象作为序列所有拥有的方法

PySequenceMethods *tp_as_sequence;

//一个对象作为映射所有拥有的方法

PyMappingMethods *tp_as_mapping;

/* More standard operations (here for binary compatibility) */

//内部的__hash__方法

//typedef Py_hash_t (*hashfunc)(PyObject *);

hashfunc tp_hash;

//内部的__call__方法

//typedef PyObject * (*ternaryfunc)(PyObject *, PyObject *, PyObject *);

ternaryfunc tp_call;

//内部的__repr__方法

//typedef PyObject *(*reprfunc)(PyObject *);

reprfunc tp_str;

//获取属性

//typedef PyObject *(*getattrofunc)(PyObject *, PyObject *);

getattrofunc tp_getattro;

//设置属性

//typedef int (*setattrofunc)(PyObject *, PyObject *, PyObject *);

setattrofunc tp_setattro;

//作为缓存,不需要关心

/*

typedef struct {

getbufferproc bf_getbuffer;

releasebufferproc bf_releasebuffer;

} PyBufferProcs;

*/

PyBufferProcs *tp_as_buffer;

//这个类的特点,比如:

//Py_TPFLAGS_HEAPTYPE:是否在堆区申请空间

//Py_TPFLAGS_BASETYPE:是否允许这个类被其它类继承

//Py_TPFLAGS_IS_ABSTRACT:是否为抽象类

//Py_TPFLAGS_HAVE_GC:是否被垃圾回收跟踪

//这里面有很多,具体可以去object.h中查看

//一般我们设置成Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC即可

unsigned long tp_flags;

//这个类的注释

const char *tp_doc; /* Documentation string */

//用于检测是否出现循环引用,和下面的tp_clear是一组

/*

class A:

pass

a = A()

a.attr = a

此时就会出现循环引用

*/

//typedef int (*traverseproc)(PyObject *, visitproc, void *);

traverseproc tp_traverse;

//删除对包含对象的引用

inquiry tp_clear;

//富比较

//typedef PyObject *(*richcmpfunc) (PyObject *, PyObject *, int);

richcmpfunc tp_richcompare;

//弱引用,不需要关心

Py_ssize_t tp_weaklistoffset;

//__iter__方法

//typedef PyObject *(*getiterfunc) (PyObject *);

getiterfunc tp_iter;

//__next__方法

//typedef PyObject *(*iternextfunc) (PyObject *);

iternextfunc tp_iternext;

/* Attribute descriptor and subclassing stuff */

//内部的方法,这个PyMethodDef不陌生了吧

struct PyMethodDef *tp_methods;

//内部的成员

struct PyMemberDef *tp_members;

//一个结构体,包含了name、get、set、doc、closure

struct PyGetSetDef *tp_getset;

//继承的基类

struct _typeobject *tp_base;

//内部的属性字典

PyObject *tp_dict;

//描述符,__get__方法

//typedef PyObject *(*descrgetfunc) (PyObject *, PyObject *, PyObject *);

descrgetfunc tp_descr_get;

//描述符,__set__方法

//typedef int (*descrsetfunc) (PyObject *, PyObject *, PyObject *);

descrsetfunc tp_descr_set;

//生成的实例对象是否有属性字典

//我们上一个例子中的实例对象显然是没有属性字典的,因为我们当时没有设置这个成员

Py_ssize_t tp_dictoffset;

//初始化函数

//typedef int (*initproc)(PyObject *, PyObject *, PyObject *);

initproc tp_init;

//为实例对象分配空间的函数

//typedef PyObject *(*allocfunc)(struct _typeobject *, Py_ssize_t);

allocfunc tp_alloc;

//__new__方法

//typedef PyObject *(*newfunc)(struct _typeobject *, PyObject *, PyObject *);

newfunc tp_new;

//我们一般设置到tp_new即可,剩下的就不需要管了

//释放一个实例对象

//typedef void (*freefunc)(void *); 一般会在析构函数中调用

freefunc tp_free; /* Low-level free-memory routine */

//typedef int (*inquiry)(PyObject *); 是否被gc跟踪

inquiry tp_is_gc; /* For PyObject_IS_GC */

//继承哪些类,这里可以指定继承多个类

//这个还是有必要的,因此这个可以单独设置

PyObject *tp_bases;

//下面的就不需要关心了

PyObject *tp_mro; /* method resolution order */

PyObject *tp_cache;

PyObject *tp_subclasses;

PyObject *tp_weaklist;

destructor tp_del;

unsigned int tp_version_tag;

destructor tp_finalize;

} PyTypeObject;

这里面我们看到有很多成员,如果有些成员我们不需要的话,那么就设置为0即可。不过即便设置为0,但是有些成员我们在调用PyType_Ready初始化的时候,也会设置进去。比如tp_dict,这个我们创建类的时候没有设置,但是这个类是有属性字典的,因为在PyType_Ready中设置了;但有的不会,比如tp_dictoffset,这个我们没有设置,那么类在PyType_Ready中也不会设置,因此这个类的实例对象,就真的没有属性字典了。再比如tp_free,我们也没有设置,但是是可以调用的,原因你懂的。

使用python源码的方式创建

下面我们就来按照python源码创建内建的类的方式,来创建一个python中的类。

#include "Python.h"

typedef struct{

PyObject_HEAD

}MyClass;

static PyObject *

MyClass_new(PyTypeObject *cls, PyObject *args, PyObject *kw)

{

MyClass *self = (MyClass *)cls -> tp_alloc(cls, 0); //此时就由python管理了

return (PyObject *)self;

}

static int

MyClass_init(PyObject *self, PyObject *args, PyObject *kw)

{

char *name;

int age;

char *gender;

char *keys[] = {"name", "age", "gender", NULL};

if (!PyArg_ParseTupleAndKeywords(args, kw, "sis", keys, &name, &age, &gender)){

return -1;

}

printf("name = %s, age = %d, gender = %s\n", name, age, gender);

return 0;

}

void

MyClass_del(PyObject *self)

{

Py_TYPE(self) -> tp_free(self);

}

//增加一个__call__方法

static PyObject *

MyClass_call(PyObject *self, PyObject *args, PyObject *kw)

{

return PyUnicode_FromString("__call__方法被调用啦~~~");

}

static PyTypeObject my_class = {

PyVarObject_HEAD_INIT(&PyType_Type, 0)

"MyClass", /* tp_name */

sizeof(MyClass), /* tp_basicsize */

0, /* tp_itemsize */

MyClass_del, /* 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 */

MyClass_call, /* tp_call */

0, /* tp_str */

0, /* tp_getattro */

0, /* tp_setattro */

0, /* tp_as_buffer */

0, /* tp_flags */

"this is a class", /* 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 */

MyClass_init, /* tp_init */

0, /* tp_alloc */

MyClass_new, /* tp_new */

0, /* tp_free */

};

static PyModuleDef HANSER = {

PyModuleDef_HEAD_INIT,

"hanser",

"this is a module named hanser",

-1,

0,

NULL,

NULL,

NULL,

NULL

};

PyMODINIT_FUNC

PyInit_hanser(void)

{

if (PyType_Ready(&my_class) < 0){

return NULL;

}

Py_XINCREF(&my_class);

PyObject *m = PyModule_Create(&HANSER);

PyModule_AddObject(m, "MyClass", (PyObject *)&my_class);

return m;

}

import hanser

self = hanser.MyClass("mashiro", 16, "female")

"""

name = mashiro, age = 16, gender = female

"""

print(hanser.MyClass.__doc__) # this is a class

print(self()) # __call__方法被调用啦~~~

因此我们就再次实现了在C中创建一个python的类,并且目前是没有内存泄露的。

创建PyTypeObject模板

创建一个PyTypeObject对象我们可以就按照python源码中显示的来。

static PyTypeObject xxx = {

PyVarObject_HEAD_INIT(&PyType_Type, 0)

"xxx", /* tp_name */

sizeof(xxx), /* tp_basicsize */

0, /* tp_itemsize */

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 */

0, /* 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 */

0, /* tp_new */

0, /* tp_free */

};

需要啥,就填啥即可。

给类添加成员和方法

添加成员

我们目前创建的类光秃秃的,啥也没有,下面我们就来使它变得饱满。

#include "Python.h"

#include "structmember.h" //添加成员需要导入这个头文件

//添加成员,这里面的参数要和python中__init__中的参数保持一致

//你可以把name、age、gender看成是要通过self.的方式来设置的

//假设这里面没有gender,那么即使python中传了gender这个参数、并且解析出来了

//但是你没法设置,所以实例化的对象依旧无法访问

typedef struct{

PyObject_HEAD

PyObject *name;

PyObject *age;

PyObject *gender;

}MyClass;

//添加成员,这是一个PyNumberDef类型的数组,然后显然要把数组名放到类的tp_members中

//PyNumberDef结构体有以下成员:name type offset flags doc

static PyMemberDef members[] = {

//这些成员具体值是什么?我们需要在MyClass_init中设置

{

"name", //成员名

T_OBJECT_EX, //类型,关于类型我们只需要记住以下几种:T_INT T_FLOAT T_DOUBLE T_STRING T_OBJECT_EX T_CHAR

//接收结构体对象和一个成员

offsetof(MyClass, name), //对应值的偏移地址,由于python中的类是动态变化的,所以C只能通过偏移的地址来找到对应的成员,offsetof是一个宏

0, //变量的读取类型,设置为0表示可读写,还可以可以设置成READONLY

"this is a name" //成员说明

},

{"age", T_OBJECT_EX, offsetof(MyClass, age), 0, "this is a age"},

{"gender", T_OBJECT_EX, offsetof(MyClass, gender), 0, "this is a gender"},

//结尾有一个{NULL}

{NULL}

};

static PyObject *

MyClass_new(PyTypeObject *cls, PyObject *args, PyObject *kw)

{

MyClass *self = (MyClass *)cls -> tp_alloc(cls, 0); //此时就由python管理了

return (PyObject *)self;

}

//构造函数接收三个PyObject *

static int

MyClass_init(PyObject *self, PyObject *args, PyObject *kw)

{

char *name;

int age;

char *gender;

char *keys[] = {"name", "age", "gender", NULL};

if (!PyArg_ParseTupleAndKeywords(args, kw, "sis", keys, &name, &age, &gender)){

return -1;

}

//这里就是设置__init__属性的,将解析出来的参数设置到__init__中,否则打印为None

//注意PyObject *要转成MyClass *,并且考虑优先级,我们需要使用括号括起来

((MyClass *)self) -> name = PyUnicode_FromString(name);

((MyClass *)self) -> age = PyLong_FromLong(age);

((MyClass *)self) -> gender = PyUnicode_FromString(gender);

//此时我们的构造函数就设置完成了

return 0;

}

void

MyClass_del(PyObject *self)

{

Py_TYPE(self) -> tp_free(self);

}

static PyObject *

MyClass_call(PyObject *self, PyObject *args, PyObject *kw)

{

return PyUnicode_FromString("__call__方法被调用啦~~~");

}

static PyTypeObject my_class = {

PyVarObject_HEAD_INIT(&PyType_Type, 0)

"MyClass", /* tp_name */

sizeof(MyClass), /* tp_basicsize */

0, /* tp_itemsize */

MyClass_del, /* 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 */

MyClass_call, /* tp_call */

0, /* tp_str */

0, /* tp_getattro */

0, /* tp_setattro */

0, /* tp_as_buffer */

0, /* tp_flags */

"this is a class", /* tp_doc */

0, /* tp_traverse */

0, /* tp_clear */

0, /* tp_richcompare */

0, /* tp_weaklistoffset */

0, /* tp_iter */

0, /* tp_iternext */

0, /* tp_methods */

members, /* tp_members */

0, /* tp_getset */

0, /* tp_base */

0, /* tp_dict */

0, /* tp_descr_get */

0, /* tp_descr_set */

0, /* tp_dictoffset */

MyClass_init, /* tp_init */

0, /* tp_alloc */

MyClass_new, /* tp_new */

0, /* tp_free */

};

static PyModuleDef HANSER = {

PyModuleDef_HEAD_INIT,

"hanser",

"this is a module named hanser",

-1,

0,

NULL,

NULL,

NULL,

NULL

};

PyMODINIT_FUNC

PyInit_hanser(void)

{

if (PyType_Ready(&my_class) < 0){

return NULL;

}

Py_XINCREF(&my_class);

PyObject *m = PyModule_Create(&HANSER);

PyModule_AddObject(m, "MyClass", (PyObject *)&my_class);

return m;

}

import hanser

cls = hanser.MyClass

self = cls("古明地觉", 16, "female")

print(self.name, self.age, self.gender) # 古明地觉 16 female

# 但是一个比较神奇的地方是,我们使用类来调用不会报错,而是告诉我们这还少类的实例对象的一个成员

print(cls.name, cls.age, cls.gender) #

添加方法

#include "Python.h"

#include "structmember.h"

typedef struct{

PyObject_HEAD

PyObject *name;

PyObject *age;

PyObject *gender;

}MyClass;

//下面来给类添加方法啦,添加方法跟之前的创建函数是一样的

static PyObject *

age_incr_1(PyObject *self, PyObject *args, PyObject *kw)

{

int age;

char *keys[] = {"age", NULL};

if (!PyArg_ParseTupleAndKeywords(args, kw, "i", keys, &age)){

return NULL;

}

age++;

((MyClass *)self) -> age = PyLong_FromLong(age);

return Py_None;

}

//构建PyMethodDef[], 方法和之前创建函数是一样的,但是这是类的方法,记得添加到类的tp_methods成员中

static PyMethodDef MyClass_methods[] = {

{"age_incr_1", (PyCFunction)age_incr_1, METH_VARARGS | METH_KEYWORDS, "method age_incr_1"},

{NULL, NULL}

};

static PyMemberDef members[] = {

{

"name",

T_OBJECT_EX,

offsetof(MyClass, name),

0,

"this is a name"

},

{"age", T_OBJECT_EX, offsetof(MyClass, age), 0, "this is a age"},

{"gender", T_OBJECT_EX, offsetof(MyClass, gender), 0, "this is a gender"},

{NULL}

};

static PyObject *

MyClass_new(PyTypeObject *cls, PyObject *args, PyObject *kw)

{

MyClass *self = (MyClass *)cls -> tp_alloc(cls, 0);

return (PyObject *)self;

}

static int

MyClass_init(PyObject *self, PyObject *args, PyObject *kw)

{

char *name;

int age;

char *gender;

char *keys[] = {"name", "age", "gender", NULL};

if (!PyArg_ParseTupleAndKeywords(args, kw, "sis", keys, &name, &age, &gender)){

return -1;

}

((MyClass *)self) -> name = PyUnicode_FromString(name);

((MyClass *)self) -> age = PyLong_FromLong(age);

((MyClass *)self) -> gender = PyUnicode_FromString(gender);

return 0;

}

void

MyClass_del(PyObject *self)

{

Py_TYPE(self) -> tp_free(self);

}

static PyObject *

MyClass_call(PyObject *self, PyObject *args, PyObject *kw)

{

return PyUnicode_FromString("__call__方法被调用啦~~~");

}

static PyTypeObject my_class = {

PyVarObject_HEAD_INIT(&PyType_Type, 0)

"MyClass", /* tp_name */

sizeof(MyClass), /* tp_basicsize */

0, /* tp_itemsize */

MyClass_del, /* 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 */

MyClass_call, /* tp_call */

0, /* tp_str */

0, /* tp_getattro */

0, /* tp_setattro */

0, /* tp_as_buffer */

0, /* tp_flags */

"this is a class", /* tp_doc */

0, /* tp_traverse */

0, /* tp_clear */

0, /* tp_richcompare */

0, /* tp_weaklistoffset */

0, /* tp_iter */

0, /* tp_iternext */

MyClass_methods, /* tp_methods */

members, /* tp_members */

0, /* tp_getset */

0, /* tp_base */

0, /* tp_dict */

0, /* tp_descr_get */

0, /* tp_descr_set */

0, /* tp_dictoffset */

MyClass_init, /* tp_init */

0, /* tp_alloc */

MyClass_new, /* tp_new */

0, /* tp_free */

};

static PyModuleDef HANSER = {

PyModuleDef_HEAD_INIT,

"hanser",

"this is a module named hanser",

-1,

0,

NULL,

NULL,

NULL,

NULL

};

PyMODINIT_FUNC

PyInit_hanser(void)

{

if (PyType_Ready(&my_class) < 0){

return NULL;

}

Py_XINCREF(&my_class);

PyObject *m = PyModule_Create(&HANSER);

PyModule_AddObject(m, "MyClass", (PyObject *)&my_class);

return m;

}

import hanser

cls = hanser.MyClass

self = cls("古明地觉", 16, "female")

print(self.age) # 16

self.age_incr_1(self.age)

print(self.age) # 17

以上我们就实现了如何在C中定义一个python的类,我们看到使用C来实现确实够麻烦的。所以我个人觉得,至少就我个人而言,一般不太会使用C来编写扩展模块,主要是太麻烦了,如果实现复杂的功能,那么你需要了解python底层大量的api,需要的代价太大了。如果需要效率,我个人更倾向于使用C来编写动态链接库的方式,然后使用ctypes去调用。

而且我们后面会介绍golang编写动态链接库交给python去调用,golang的效率虽然比不过C,但是也差不了多少,重点是golang带垃圾回收、而且开发速度上也比动态语言差不了多少。因此这篇博客更倾向于介绍python的底层,至于是否使用C来编写扩展模块,就由你自己来决定。而且使用C来编写,需要你有很强的C语言的知识,以及python源码的知识,我们目前这里只是介绍了很小的一部分。python中很多其它的东西在底层如何实现我们都没有介绍,比如:如何导入一个模块,如何在底层使用python的内置函数,python的生成器、列表解析怎么实现等等等等一大堆。这些需要你自己去了解了

因此是否使用扩展模块,我个人给出一个标准。如果你发现有大量的工作需要使用原生的C来实现才能保证高效率,但同时又需要和python直接交互,那么推荐你使用扩展模块的方式;如果你发现C没有做太多的事情,只是把Python创建函数、类的过程用底层重新翻译了一遍,或者说做了很多事情、但是不需要和python进行交互,我们可以使用ctypes获取返回的结果即可,那么完全没有必要使用扩展模块的方式。还是那句话,这篇博客只是一个引导作用,它不足以支持你在工作中编写扩展模块实现复杂的功能,更复杂的用法需要你自己再去了解。

因此折中的办法就是:你可以使用python编写,然后使用Cython加速,同样编译成python的pyd或者so,而且使用python编写效率会大大提高;还有就是使用C或者golang编写动态链接库,这样只需要你有静态语言的知识、不需要你有python源码的知识,只需要会使用ctypes调用即可。

类的循环引用

解决类的循环引用需要实现PyTypeObject的两个成员,tp_traverse(检测是否有循环引用)和tp_clear(清除引用计数)。

static int

MyClass_traverse(PyObject *self, visitproc visit, void *args)

{

//提供了一个宏,检测是否有循环引用

Py_VISIT(PyObject *); //具体检测谁,根据代码情况

//返回0表示成功

return 0;

}

static int

MyClass_clear(PyObject *self)

{

//同样提供了一个宏,进行引用计数的清理

Py_CLEAR(PyObject *); //具体清理谁,根据代码情况

}

//另外我们在析构函数中,其实还少了一步

void

MyClass_del(PyObject *self)

{

//我们说python会跟踪创建的对象,如果被回收了,那么应该从链表中移除

PyObject_GC_UnTrack(self);

Py_TYPE(self) -> tp_free(self);

}

my_class -> tp_traverse = MyClass_traverse;

my_class -> tp_clear = MyClass_clear;

有兴趣可以"杀进"源码中自己查看。

以上です

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Go语言(也称为Golang)是由Google开发的一种静态强型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值