使用C语言编写Python扩展——创建自定义类型(1)
2017-05-26 14:21
609 查看
转载地址:http://www.xefan.com/archives/84091.html在Python代码中如果要创建一个自定义类使用class关键字即可,但是在C代码中就没那么方便了。首先简单介绍下Python中的类型。在python中一切皆对象,python中有两种对象:一种是类型对象(class对象):表示Python定义的类型,例如int, str, object等;另一种是实例对象(instance对象):表示由class对象创建的实例。Python中的所有对象都是直接或者间接继承object,然后object又是typy类型。可以运行下面的例子看看输出结果:
class A(object): pass a = A() print(type(a)) print(isinstance(a, A)) print(isinstance(a, object)) print(isinstance(a, type)) print(type(A)) print(A.__base__) print(isinstance(A, object)) print(isinstance(A, type)) print(type(object)) print(isinstance(object, type)) print(type(type)) print(isinstance(type, object))python是一门面向对象的编程语言,它是用C写的,而C又是面向过程的编程语言,那么python的类在C中是如何实现的呢?答案就是用结构体来模拟。在Python的object.h头文件中定义了一个重要的结构体 PyTypeObject 。创建新的类型就是靠的它,该结构体定义如下:
typedef struct _typeobject { PyObject_VAR_HEAD char *tp_name; /* For printing, in format "<module>.<name>" */ int tp_basicsize, tp_itemsize; /* For allocation */ /* Methods to implement standard operations */ destructor tp_dealloc; printfunc tp_print; getattrfunc tp_getattr; setattrfunc tp_setattr; cmpfunc tp_compare; reprfunc tp_repr; /* Method suites for standard classes */ PyNumberMethods *tp_as_number; PySequenceMethods *tp_as_sequence; PyMappingMethods *tp_as_mapping; /* More standard operations (here for binary compatibility) */ hashfunc tp_hash; ternaryfunc tp_call; reprfunc tp_str; getattrofunc tp_getattro; setattrofunc tp_setattro; /* Functions to access object as input/output buffer */ PyBufferProcs *tp_as_buffer; /* Flags to define presence of optional/expanded features */ long tp_flags; char *tp_doc; /* Documentation string */ /* Assigned meaning in release 2.0 */ /* call function for all accessible objects */ traverseproc tp_traverse; /* delete references to contained objects */ inquiry tp_clear; /* Assigned meaning in release 2.1 */ /* rich comparisons */ richcmpfunc tp_richcompare; /* weak reference enabler */ long tp_weaklistoffset; /* Added in release 2.2 */ /* Iterators */ getiterfunc tp_iter; iternextfunc tp_iternext; /* Attribute descriptor and subclassing stuff */ struct PyMethodDef *tp_methods; struct PyMemberDef *tp_members; struct PyGetSetDef *tp_getset; struct _typeobject *tp_base; PyObject *tp_dict; descrgetfunc tp_descr_get; descrsetfunc tp_descr_set; long tp_dictoffset; initproc tp_init; allocfunc tp_alloc; newfunc tp_new; freefunc tp_free; /* Low-level free-memory routine */ 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; } PyTypeObject;这个比较庞大,里面包含的数据比较多,大部分都是一些函数指针而且可以为空,至于每个字段是什么意思请查看Python文档。
创建自定义类型
创建一个新的C代码文件 noddy.c ,然后我们编写一个名为 noddy 的扩展模块,该模块包含了一个名为 Noddy 的类。首先创建一个新的 PyTypeObject 类型的变量:typedef struct { PyObject_HEAD /* Type-specific fields go here. */ } noddy_NoddyObject; static PyTypeObject noddy_NoddyType = { PyVarObject_HEAD_INIT(NULL, 0) "noddy.Noddy", /*tp_name*/ sizeof(noddy_NoddyObject), /*tp_basicsize*/ 0, /*tp_itemsize*/ 0, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ 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, /*tp_flags*/ "Noddy objects", /*tp_doc*/ };这里是定义了一个noddy_NoddyObject结构体,它的第一个字段为 PyObject_HEAD ,因此相当于一个PyObject类型;然后还有一个 noddy_NoddyType 变量,它的第一个字段为 PyVarObject_HEAD_INIT(NULL,0) ,这个很很重要,按理说这个应该写成PyVarObject_HEAD_INIT(&PyType_Type, 0) ,即表示Noddy这个类是一个type类型的对象。不过有的C编译器会对这个报错,因此这一项将在后面调用PyType_Ready函数来填充。noddy_NoddyType 即是 Noddy 类,它保存了该类的元信息;noddy_NoddyObject结构体用于保存该类的实例对象的数据。只要是定义的结构体以PyObject_HEAD开始就属于是一个PyObject类型。PyObject_VAR_HEAD与PyObject_HEAD相似,只是PyObject_HEAD表示的是该类型占用内存大小是固定的如int、float;而PyObject_VAR_HEAD表示该类型占用的内存是可变的如list、dict。然后创建一个新扩展模块,并完成初始化:
static PyMethodDef noddy_methods[] = { {NULL} /* Sentinel */ }; PyMODINIT_FUNC initnoddy(void) { PyObject* m; noddy_NoddyType.tp_new = PyType_GenericNew; if (PyType_Ready(&noddy_NoddyType) < 0) return; m = Py_InitModule3("noddy", noddy_methods, "Example module that creates an extension type."); Py_INCREF(&noddy_NoddyType); PyModule_AddObject(m, "Noddy", (PyObject *)&noddy_NoddyType); }注意:以上是针对Python2的,在Python3中模块的初始化操作略有不同。请参考第一节的内容。noddy_NoddyType即是我们要创建的 Noddy 类,它是 PyTypeObject 类型的结构变量。为了创建新的类型,我们需要指明 tp_new 方法,它相当于Python中的 __new__,这里我们使用默认的 PyType_GenericNew 即可。然后调用 PyType_Ready 完成新类型的创建。最后调用 PyModule_AddObject 在该模块中添加刚刚创建的新类型。
测试
最后就是编写一个小程序来测试刚刚的模块是否可用。import noddy o = noddy.Noddy() print(o) print(type(o), type(noddy.Noddy)) # 这个会报错,noddy.Noddy 类不能被继承 class A(noddy.Noddy):
pass[/code]
相关文章推荐
- 使用C语言编写Python扩展——创建自定义类型(2)
- 使用C语言编写Python扩展1——Hello World
- 使用C语言扩展Python(三)
- 使用C语言和DX库编写东方风格的STG射击游戏--------第一章--------创建基本界面以及可以移动的角色
- 使用C语言扩展Python(一)
- 使用C语言扩展Python(一)
- dhl: 在 ASP.NET MVC 中创建自定义 HtmlHelper 使用扩展方法创建 HTML Helper
- 使用java编写SmartFoxServer自定义安全验证登录扩展
- 使用java编写SmartFoxServer自定义安全验证登录扩展
- FlowChart.NET使用教程:创建自定义节点类型(二)
- 使用C语言扩展Python(二)
- 使用C语言扩展Python(三)
- 使用SWIG轻松编写Python扩展
- 一个使用c++在lua中创建自定义数据类型的简易方法
- 使用C语言扩展Python(四)
- 使用C语言扩展Python(一)
- 使用C语言扩展Python(五)
- FlowChart.NET使用教程:创建自定义节点类型(一)
- 用C语言编写Python扩展模块
- 使用C语言扩展Python