C言語は、Python動的モジュール(3)で記述されている - PythonのクラスはCで実装します

ウェッジ

我々はCタイプパイソンを紹介し、この時間は達成する方法である、我々はC Pythonのint型のオブジェクトで作成された、あなたはPyList_New Pythonの使用できるオブジェクトのリストを作成し、PyLong_FromLongを使用することができ、その後、どのようにCででのpythonを構築しますそれを好きですか?

クラスの構築のために、我々は間違いなく、次のステップが必要になります。

  • 创建一个类扩展
  • 添加类的参数
  • 添加类的方法
  • 添加类的属性,比如可以设置、获取属性
  • 添加类的继承
  • 解决类的循环引用导致的内存泄露问题和自定义垃圾回收

最初のいくつかのステップが必要ですが、最後の質問は無視することは容易です。我々は、我々が検討してあなたの質問のメモリを必要としないのpythonモジュールを書きますが、拡張モジュールを書くときに、メモリの側面が自分自身を管理する必要があるので、我々は、Cで書かれています。
Pythonはオブジェクトを回復する必要があるかどうかを判断するために参照カウントであり、これは最も原始的な、最も簡単であるが、それはまた、最も便利な方法です。それはシンプルかつ直感的であるため、その多くの欠点、Pythonやこの方法の使用にもかかわらず。我々が言ったようにしかし、また、それはない存在、赤字が問題は、循環参照を解決できないということであるPythonのGCはこの事を行うことですので、3点のリスト生成技術により、オブジェクトのライフサイクルに基づいて部門、そして、その参照カウントを変更し、3つのカラーマーカーによって循環参照モデルを持っているものを見つけることができます。したがって、Pythonオブジェクトに回収される、最終的にその参照カウントがゼロに依存します。

あなたは簡単に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 "<module>.<name>" */
    //大小,用于申请空间的,注意了,这里是两个成员
    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の一部のメンバーは、また行くように設定されています。クラスはPyType_Readyでもないが、そのようなtp_dictoffsetとして、我々はこのセットを持っていないが、いくつかはしません;例えばtp_dictのために、私たちはクラスを作成し、この時間が設定されていませんが、このクラスは中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)  # <member 'name' of 'MyClass' objects> <member 'age' of 'MyClass' objects> <member 'gender' of 'MyClass' objects>

メソッドを追加

#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の使用を参照してください。私は個人的にそれを感じるように、複雑な機能の実装は、その後、あなたは根本的なPythonのAPIの多くを理解する必要がある場合は、これまで私が懸念していますように、一般的にCメジャーで書かれた拡張モジュールを使用していないが、あまりにも多くのトラブルは、あるとして、少なくともでは、コストがあまり必要A。効率のために必要ならば、私は個人的には、ダイナミックリンクライブラリを書くために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 *); //具体清理谁,根据代码情况
}

my_class -> tp_traverse = MyClass_traverse;
my_class -> tp_clear = MyClass_clear;

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

以上です

おすすめ

転載: www.cnblogs.com/traditional/p/12283903.html