python好多模块和c相识_快速实现python c扩展模块
1 ?python擴展模塊的組成
在python中,對于一些和系統相關的模塊或者對性能要求很高的模塊,通常會把這個模塊C化。擴展模塊中主要包含下面幾個部分:
init函數,函數名為:init+模塊名,這個函數負責初始化模塊,包括設置模塊中的方法、對象和其它相關數據的初始化。這個函數是必須的,在腳本中第一次導入這個模塊的時候,會先執行這個方法。
定義模塊方法描述表,它是一個static類型的PyMethodDef數據結構,用來描述模塊中定義的方法。
C函數定義,這些函數是方法描述表中方法的具體實現。
如果模塊中定義了類,那么還要定義類方法描述表和對象類型。
2 ?實現python擴展模塊實例
當然,有了上面的組成部分,你還是不知道怎么實現一個模塊,下面就用官方的一個例子來演示怎么實現一個python擴展模塊,這個擴展模塊用來實現在python中執行命令行命令。
// spam.c
1 #include "Python.h"
2
3 static PyObject *SpamError;4
5 static PyObject *
6 spam_system(PyObject *self, PyObject *args)7 {8 const char *command;9 intsts;10
11 if (!PyArg_ParseTuple(args, "s", &command))12 returnNULL;13 sts =system(command);14 if (sts < 0) {15 PyErr_SetString(SpamError, "System command failed");16 returnNULL;17 }18 returnPyLong_FromLong(sts);19 }20
21 static PyMethodDef SpamMethods[] ={22 {"system", spam_system, METH_VARARGS,23 "Execute a shell command."},24 {NULL, NULL, 0, NULL} /*Sentinel*/
25 };26
27 PyMODINIT_FUNC28 initspam(void)29 {30 PyObject *m;31
32 m = Py_InitModule("spam", SpamMethods);33 if (m ==NULL)34 return;35
36 SpamError = PyErr_NewException("spam.error", NULL, NULL);37 Py_INCREF(SpamError);38 PyModule_AddObject(m, "error", SpamError);39 }
上面的initspam是模塊的初始化函數,函數開始調用了Py_InitModule初始化了一個名為spam的模塊,模塊的方法描述表是SpamMethods,它描述了模塊有個名為system的方法,這個方法的c/c++實現是spam_system函數。從spam_system函數可以看到它就是調用system函數執行從python傳過來的命令。有了上面的代碼,我們怎樣在python中使用了?很簡單,先將上面代碼編譯成動態鏈接庫,然后直接在python中用import語句導入這個模塊就可以用了。在Windows下的用vs編譯就行,不過在vs建立了dll工程后,需要設置下工程的屬性,目的是設置python擴展涉及到的頭文件路徑和動態庫。具體設置如下:先在VC++目錄中設置include和lib路徑,然后在鏈接器的附加依賴項中添加python27.lib庫。
設置好后直接編譯就可以了,將編譯生成的dll文件后綴名改成pyd,然后就可以在python中直接用import導入這個模塊了。是不是非常的簡單!!!!
3 ?實現python擴展模塊中定義類
上面的實現是在模塊中定義函數來實現執行命令行命令,我們也可以在模塊中定義類,然后用類的方法來執行這個命令。代碼如下:
// spam.c
1 #include "Python.h"
2
3 static PyObject *SpamError;4
5 static PyObject *
6 spam_system(PyObject *self, PyObject *args)7 {8 const char *command;9 intsts;10
11 if (!PyArg_ParseTuple(args, "s", &command))12 returnNULL;13 sts =system(command);14 if (sts < 0) {15 PyErr_SetString(SpamError, "System command failed");16 returnNULL;17 }18 returnPyLong_FromLong(sts);19 }20
21 static PyMethodDef SpamMethods[] ={22 {"system", spam_system, METH_VARARGS,23 "Execute a shell command."},24 {NULL, NULL, 0, NULL} /*Sentinel*/
25 };26
27 PyTypeObject *SpamType =NULL;28
29 PyMODINIT_FUNC30 initspam(void)31 {32 static PyTypeObject _SpamType ={33 PyObject_HEAD_INIT(NULL)34 0, //ob_size
35 "spam.Spam", //tp_name
36 sizeof(PyObject), //tp_basicsize
37 0, //tp_itemsize
38 0, //tp_dealloc
39 0, //tp_print
40 0, //tp_getattr
41 0, //tp_setattr
42 0, //tp_compare
43 0, //tp_repr
44 0, //tp_as_number
45 0, //tp_as_sequence
46 0, //tp_as_mapping
47 0, //tp_hash
48 0, //tp_call
49 0, //tp_str
50 0, //tp_getattro
51 0, //tp_setattro
52 0, //tp_as_buffer
53 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE , //tp_flags
54 0, //tp_doc
55 0, //tp_traverse
56 0, //tp_clear
57 0, //tp_richcompare
58 0, //tp_weaklistoffset
59 0, //tp_iter
60 0, //tp_iternext
61 SpamMethods, //tp_methods
62 0, //tp_members
63 0, //tp_getset
64 0, //tp_base
65 0, //tp_dict
66 0, //tp_descr_get
67 0, //tp_descr_set
68 0, //tp_dictoffset
69 0, //tp_init
70 0, //tp_alloc
71 PyType_GenericNew, //tp_new
72 };73
74 PyObject *m;75
76 m = Py_InitModule("spam", NULL);77 if (m ==NULL)78 return;79 if (PyType_Ready(&_SpamType) < 0)80 return;81 SpamType = &_SpamType;82 Py_INCREF(SpamType);83 PyModule_AddObject(m, "Spam", (PyObject*)SpamType);84 SpamError = PyErr_NewException("spam.error", NULL, NULL);85 Py_INCREF(SpamError);86 PyModule_AddObject(m, "error", SpamError);87 }
上面的代碼與之前的代碼只是多了個Spam類的定義,使用的時候通過Spam的實例化對象來調用system函數。
4 ?Python/C API涉及的引用計數問題
通過上面的例子,是不是覺得寫python的C擴展模塊非常的簡單呢?其實不然,主要是python中有個引用計數問題,在寫擴展模塊的時候必須非常小心的處理,否則很有容易導致內存泄露。根據python官方的定義,在Python/C API中,引用計數的行為被歸納為三種:new reference、borrow reference和steal reference,前兩種用于描述返回PyObject*類型的函數對返回的這個對象的引用計數的行為;后一種用于將一個PyObject*類型傳入函數后,函數對這個對象的引用計數的行為。new referenc表示函數將這個對象引用的所有權轉交給函數調用者了,由函數的調用者來管理這個引進的計數,也就是說調用者不用這個引用的時候必須顯示的調用?Py_DECREF()或者Py_XDECREF()來釋放這個引用,典型的函數是PyObject_、PyNumber_、PySequence_和PyMapping_;borrow reference與new reference剛好相反,表示函數的調用者只管用這個引用,不用關心它的引用計數,用完了也不用顯示調用Py_DECREF()或者Py_XDECREF()來釋放這個引用,典型的函數是PyList_GetItem、PyTuple_GetItem;steal reference表示函數內部只會使用這個引用,不會調用Py_INCREF來增加這個引用的引用計數,相當于“偷了”被調用者的一個引用計數,典型的函數是PyList_SetItem()和PyTuple_SetItem()。因此,在編寫C擴展的時,如果遇到某個Python/C API不確定是哪種reference的時候,建議查下官方文檔,文檔中會明確的說明這個函數是哪類reference(如下圖所示),這樣能大大減少引用計數的問題。
5 ?參考
總結
以上是生活随笔為你收集整理的python好多模块和c相识_快速实现python c扩展模块的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 怎么办民生信用卡
- 下一篇: 怎么用盘做系统盘 用外接硬盘制作系统启动