
- Python 基礎
- Python - 首頁
- Python - 概述
- Python - 歷史
- Python - 特性
- Python vs C++
- Python - Hello World 程式
- Python - 應用領域
- Python - 直譯器
- Python - 環境搭建
- Python - 虛擬環境
- Python - 基本語法
- Python - 變數
- Python - 資料型別
- Python - 型別轉換
- Python - Unicode 系統
- Python - 字面量
- Python - 運算子
- Python - 算術運算子
- Python - 比較運算子
- Python - 賦值運算子
- Python - 邏輯運算子
- Python - 位運算子
- Python - 成員運算子
- Python - 身份運算子
- Python - 運算子優先順序
- Python - 註釋
- Python - 使用者輸入
- Python - 數字
- Python - 布林值
- Python 控制語句
- Python - 控制流
- Python - 決策
- Python - if 語句
- Python - if else
- Python - 巢狀 if
- Python - Match-Case 語句
- Python - 迴圈
- Python - for 迴圈
- Python - for-else 迴圈
- Python - while 迴圈
- Python - break 語句
- Python - continue 語句
- Python - pass 語句
- Python - 巢狀迴圈
- Python 函式與模組
- Python - 函式
- Python - 預設引數
- Python - 關鍵字引數
- Python - 僅關鍵字引數
- Python - 位置引數
- Python - 僅位置引數
- Python - 可變引數
- Python - 變數作用域
- Python - 函式註解
- Python - 模組
- Python - 內建函式
- Python 字串
- Python - 字串
- Python - 字串切片
- Python - 修改字串
- Python - 字串連線
- Python - 字串格式化
- Python - 跳脫字元
- Python - 字串方法
- Python - 字串練習
- Python 列表
- Python - 列表
- Python - 訪問列表元素
- Python - 修改列表元素
- Python - 新增列表元素
- Python - 刪除列表元素
- Python - 遍歷列表
- Python - 列表推導式
- Python - 排序列表
- Python - 複製列表
- Python - 合併列表
- Python - 列表方法
- Python - 列表練習
- Python 元組
- Python - 元組
- Python - 訪問元組元素
- Python - 更新元組
- Python - 解包元組
- Python - 遍歷元組
- Python - 合併元組
- Python - 元組方法
- Python - 元組練習
- Python 集合
- Python - 集合
- Python - 訪問集合元素
- Python - 新增集合元素
- Python - 刪除集合元素
- Python - 遍歷集合
- Python - 合併集合
- Python - 複製集合
- Python - 集合運算子
- Python - 集合方法
- Python - 集合練習
- Python 字典
- Python - 字典
- Python - 訪問字典元素
- Python - 修改字典元素
- Python - 新增字典元素
- Python - 刪除字典元素
- Python - 字典檢視物件
- Python - 遍歷字典
- Python - 複製字典
- Python - 巢狀字典
- Python - 字典方法
- Python - 字典練習
- Python 陣列
- Python - 陣列
- Python - 訪問陣列元素
- Python - 新增陣列元素
- Python - 刪除陣列元素
- Python - 遍歷陣列
- Python - 複製陣列
- Python - 反轉陣列
- Python - 排序陣列
- Python - 合併陣列
- Python - 陣列方法
- Python - 陣列練習
- Python 檔案處理
- Python - 檔案處理
- Python - 寫入檔案
- Python - 讀取檔案
- Python - 重新命名和刪除檔案
- Python - 目錄
- Python - 檔案方法
- Python - OS 檔案/目錄方法
- Python - OS 路徑方法
- 面向物件程式設計
- Python - OOPs 概念
- Python - 類與物件
- Python - 類屬性
- Python - 類方法
- Python - 靜態方法
- Python - 建構函式
- Python - 訪問修飾符
- Python - 繼承
- Python - 多型
- Python - 方法重寫
- Python - 方法過載
- Python - 動態繫結
- Python - 動態型別
- Python - 抽象
- Python - 封裝
- Python - 介面
- Python - 包
- Python - 內部類
- Python - 匿名類和物件
- Python - 單例類
- Python - 包裝類
- Python - 列舉
- Python - 反射
- Python 錯誤與異常
- Python - 語法錯誤
- Python - 異常
- Python - try-except 程式碼塊
- Python - try-finally 程式碼塊
- Python - 丟擲異常
- Python - 異常鏈
- Python - 巢狀 try 程式碼塊
- Python - 使用者自定義異常
- Python - 日誌記錄
- Python - 斷言
- Python - 內建異常
- Python 多執行緒
- Python - 多執行緒
- Python - 執行緒生命週期
- Python - 建立執行緒
- Python - 啟動執行緒
- Python - 執行緒連線
- Python - 執行緒命名
- Python - 執行緒排程
- Python - 執行緒池
- Python - 主執行緒
- Python - 執行緒優先順序
- Python - 守護執行緒
- Python - 執行緒同步
- Python 同步
- Python - 執行緒間通訊
- Python - 執行緒死鎖
- Python - 中斷執行緒
- Python 網路程式設計
- Python - 網路程式設計
- Python - 套接字程式設計
- Python - URL 處理
- Python - 泛型
- Python 庫
- NumPy 教程
- Pandas 教程
- SciPy 教程
- Matplotlib 教程
- Django 教程
- OpenCV 教程
- Python 雜項
- Python - 日期與時間
- Python - 數學
- Python - 迭代器
- Python - 生成器
- Python - 閉包
- Python - 裝飾器
- Python - 遞迴
- Python - 正則表示式
- Python - PIP
- Python - 資料庫訪問
- Python - 弱引用
- Python - 序列化
- Python - 模板
- Python - 輸出格式化
- Python - 效能測量
- Python - 資料壓縮
- Python - CGI 程式設計
- Python - XML 處理
- Python - GUI 程式設計
- Python - 命令列引數
- Python - 文件字串
- Python - JSON
- Python - 傳送郵件
- Python - 擴充套件進階
- Python - 工具/實用程式
- Python - GUI
- Python 高階概念
- Python - 抽象基類
- Python - 自定義異常
- Python - 高階函式
- Python - 物件內部
- Python - 記憶體管理
- Python - 元類
- Python - 使用元類進行超程式設計
- Python - 模擬和存根
- Python - 猴子補丁
- Python - 訊號處理
- Python - 型別提示
- Python - 自動化教程
- Python - Humanize 包
- Python - 上下文管理器
- Python - 協程
- Python - 描述符
- Python - 診斷和修復記憶體洩漏
- Python - 不可變資料結構
- Python 有用資源
- Python - 問答
- Python - 線上測驗
- Python - 快速指南
- Python - 參考
- Python - 速查表
- Python - 專案
- Python - 有用資源
- Python - 討論
- Python 編譯器
- NumPy 編譯器
- Matplotlib 編譯器
- SciPy 編譯器
Python - 擴充套件進階
使用任何編譯語言(如 C、C++ 或 Java)編寫的任何程式碼都可以整合或匯入到另一個 Python 指令碼中。此程式碼被視為“擴充套件”。
Python 擴充套件模組只不過是一個普通的 C 庫。在 Unix 機器上,這些庫通常以.so(共享物件)結尾。在 Windows 機器上,您通常會看到.dll(動態連結庫)。
編寫擴充套件的先決條件
要開始編寫擴充套件,您將需要 Python 標頭檔案。
在 Unix 機器上,這通常需要安裝特定於開發人員的軟體包。
Windows 使用者在使用二進位制 Python 安裝程式時,會將這些標頭檔案作為軟體包的一部分獲得。
此外,假設您精通 C 或 C++,可以使用 C 程式設計編寫任何 Python 擴充套件。
首先了解 Python 擴充套件
要首次瞭解 Python 擴充套件模組,您需要將程式碼分成四個部分:
標頭檔案Python.h。
您希望公開為模組介面的 C 函式。
一個表,將 Python 開發人員看到的函式名稱對映到擴充套件模組內的 C 函式。
一個初始化函式。
標頭檔案 Python.h
您需要在 C 原始檔中包含 Python.h 標頭檔案,這使您可以訪問用於將模組掛接到直譯器的內部 Python API。
確保在任何其他可能需要的標頭檔案之前包含 Python.h。您需要在包含之後跟著您想從 Python 中呼叫的函式。
C 函式
C 函式實現的簽名始終採用以下三種形式之一:
static PyObject *MyFunction(PyObject *self, PyObject *args); static PyObject *MyFunctionWithKeywords(PyObject *self, PyObject *args, PyObject *kw); static PyObject *MyFunctionWithNoArgs(PyObject *self);
前面每個宣告都返回一個 Python 物件。Python 中沒有像 C 中那樣的 void 函式。如果您不希望函式返回值,則返回 Python 的None值的 C 等價物。Python 標頭檔案定義了一個宏 Py_RETURN_NONE,它為我們執行此操作。
C 函式的名稱可以是您喜歡的任何名稱,因為它們永遠不會在擴充套件模組之外可見。它們被定義為靜態函式。
您的 C 函式通常透過組合 Python 模組和函式名稱來命名,如下所示:
static PyObject *module_func(PyObject *self, PyObject *args) { /* Do your stuff here. */ Py_RETURN_NONE; }
這是一個名為 func 的 Python 函式,位於模組 module 中。您將把指向 C 函式的指標放入模組的方法表中,該方法表通常位於原始碼的下一部分。
方法對映表
此方法表是 PyMethodDef 結構的簡單陣列。該結構看起來像這樣:
struct PyMethodDef { char *ml_name; PyCFunction ml_meth; int ml_flags; char *ml_doc; };
以下是此結構成員的描述:
ml_name - 這是函式的名稱,當 Python 直譯器在 Python 程式中使用時,它會顯示該名稱。
ml_meth - 這是具有任何一個簽名的函式的地址,如上一節所述。
ml_flags - 這告訴直譯器 ml_meth 使用哪三個簽名。
此標誌通常的值為 METH_VARARGS。
如果要允許關鍵字引數進入函式,則可以將此標誌與 METH_KEYWORDS 進行按位或運算。
這也可能具有 METH_NOARGS 的值,表示您不想接受任何引數。
mml_doc − 這是函式的文件字串,如果您不想編寫,可以為 NULL。
此表需要以哨兵結束,哨兵由相應成員的 NULL 和 0 值組成。
示例
對於上面定義的函式,我們有以下方法對映表:
static PyMethodDef module_methods[] = { { "func", (PyCFunction)module_func, METH_NOARGS, NULL }, { NULL, NULL, 0, NULL } };
初始化函式
擴充套件模組的最後一部分是初始化函式。當載入模組時,Python 直譯器會呼叫此函式。要求函式名為 initModule,其中 Module 是模組的名稱。
初始化函式需要從您將要構建的庫中匯出。Python 標頭檔案定義 PyMODINIT_FUNC 以包含適當的咒語,以便在我們要編譯的特定環境中發生這種情況。您所要做的就是在定義函式時使用它。
您的 C 初始化函式通常具有以下總體結構:
PyMODINIT_FUNC initModule() { Py_InitModule3(func, module_methods, "docstring..."); }
以下是 Py_InitModule3 函式的描述:
func − 這是要匯出的函式。
module_methods − 這是上面定義的對映表名稱。
docstring − 這是您想要在擴充套件中提供的註釋。
將所有這些放在一起,看起來如下所示:
#include <Python.h> static PyObject *module_func(PyObject *self, PyObject *args) { /* Do your stuff here. */ Py_RETURN_NONE; } static PyMethodDef module_methods[] = { { "func", (PyCFunction)module_func, METH_NOARGS, NULL }, { NULL, NULL, 0, NULL } }; PyMODINIT_FUNC initModule() { Py_InitModule3(func, module_methods, "docstring..."); }
示例
一個簡單的示例,它利用了上述所有概念:
#include <Python.h> static PyObject* helloworld(PyObject* self) { return Py_BuildValue("s", "Hello, Python extensions!!"); } static char helloworld_docs[] = "helloworld( ): Any message you want to put here!!\n"; static PyMethodDef helloworld_funcs[] = { {"helloworld", (PyCFunction)helloworld, METH_NOARGS, helloworld_docs}, {NULL} }; void inithelloworld(void) { Py_InitModule3("helloworld", helloworld_funcs, "Extension module example!"); }
這裡 Py_BuildValue 函式用於構建 Python 值。將上述程式碼儲存在 hello.c 檔案中。我們將瞭解如何編譯和安裝此模組以便從 Python 指令碼中呼叫。
構建和安裝擴充套件
distutils 包使以標準方式分發 Python 模組(純 Python 模組和擴充套件模組)變得非常容易。模組以原始碼形式分發,透過通常稱為 setup.pyas 的安裝指令碼進行構建和安裝。
對於上述模組,您需要準備以下 setup.py 指令碼:
from distutils.core import setup, Extension setup(name='helloworld', version='1.0', \ ext_modules=[Extension('helloworld', ['hello.c'])])
現在,使用以下命令,它將執行所有必要的編譯和連結步驟,使用正確的編譯器和連結器命令和標誌,並將生成的動態庫複製到適當的目錄中:
$ python setup.py install
在基於 Unix 的系統上,您很可能需要以 root 身份執行此命令才能有權寫入 site-packages 目錄。這在 Windows 上通常不是問題。
匯入擴充套件
安裝擴充套件後,您將能夠在 Python 指令碼中匯入和呼叫該擴充套件,如下所示:
import helloworld print helloworld.helloworld()
這將產生以下輸出:
Hello, Python extensions!!
傳遞函式引數
由於您很可能希望定義接受引數的函式,因此您可以為 C 函式使用其他簽名之一。例如,以下接受一些引數的函式將這樣定義:
static PyObject *module_func(PyObject *self, PyObject *args) { /* Parse args and do something interesting here. */ Py_RETURN_NONE; }
包含新函式條目的方法表將如下所示:
static PyMethodDef module_methods[] = { { "func", (PyCFunction)module_func, METH_NOARGS, NULL }, { "func", module_func, METH_VARARGS, NULL }, { NULL, NULL, 0, NULL } };
您可以使用 API PyArg_ParseTuple 函式從傳遞到 C 函式的一個 PyObject 指標中提取引數。
PyArg_ParseTuple 的第一個引數是 args 引數。這是您將要解析的物件。第二個引數是格式字串,描述了您期望引數出現的格式。每個引數在格式字串中由一個或多個字元表示,如下所示。
static PyObject *module_func(PyObject *self, PyObject *args) { int i; double d; char *s; if (!PyArg_ParseTuple(args, "ids", &i, &d, &s)) { return NULL; } /* Do something interesting here. */ Py_RETURN_NONE; }
編譯模組的新版本並匯入它使您可以使用任意數量的任意型別的引數來呼叫新函式:
module.func(1, s="three", d=2.0) module.func(i=1, d=2.0, s="three") module.func(s="three", d=2.0, i=1)
您可能還可以想出更多變化。
PyArg_ParseTuple 函式
re 是PyArg_ParseTuple 函式的標準簽名:
int PyArg_ParseTuple(PyObject* tuple,char* format,...)
此函式對於錯誤返回 0,對於成功返回非 0 值。Tuple 是作為 C 函式第二個引數的 PyObject*。這裡的 format 是一個 C 字串,描述了必選和可選引數。
以下是PyArg_ParseTuple 函式的格式程式碼列表:
程式碼 | C 型別 | 含義 |
---|---|---|
c | char | 長度為 1 的 Python 字串變為 C char。 |
d | double | Python float 變為 C double。 |
f | float | Python float 變為 C float。 |
i | int | Python int 變為 C int。 |
l | long | Python int 變為 C long。 |
L | long long | Python int 變為 C long long。 |
O | PyObject* | 獲取對 Python 引數的非 NULL 借用引用。 |
S | char* | 沒有嵌入空字元的 Python 字串到 C char*。 |
s# | char*+int | 任何 Python 字串到 C 地址和長度。 |
t# | char*+int | 只讀單段緩衝區到 C 地址和長度。 |
u | Py_UNICODE* | 沒有嵌入空字元的 Python Unicode 到 C。 |
u# | Py_UNICODE*+int | 任何 Python Unicode C 地址和長度。 |
w# | char*+int | 讀/寫單段緩衝區到 C 地址和長度。 |
z | char* | 類似於 s,也接受 None(將 C char* 設定為 NULL)。 |
z# | char*+int | 類似於 s#,也接受 None(將 C char* 設定為 NULL)。 |
(...) | 根據 ... | Python 序列被視為每個專案一個引數。 |
| | 以下引數是可選的。 | |
: | 格式結束,後跟錯誤訊息的函式名稱。 | |
; | 格式結束,後跟完整的錯誤訊息文字。 |
返回值
Py_BuildValue 使用與 PyArg_ParseTuple 非常相似的格式字串。您不是傳入要構建的值的地址,而是傳入實際的值。以下是一個示例,展示瞭如何實現 add 函式。
static PyObject *foo_add(PyObject *self, PyObject *args) { int a; int b; if (!PyArg_ParseTuple(args, "ii", &a, &b)) { return NULL; } return Py_BuildValue("i", a + b); }
如果用 Python 實現,它將如下所示:
def add(a, b): return (a + b)
您可以從函式中返回兩個值,如下所示。這將使用 Python 中的列表捕獲。
static PyObject *foo_add_subtract(PyObject *self, PyObject *args) { int a; int b; if (!PyArg_ParseTuple(args, "ii", &a, &b)) { return NULL; } return Py_BuildValue("ii", a + b, a - b); }
如果用 Python 實現,它將如下所示:
def add_subtract(a, b): return (a + b, a - b)
Py_BuildValue 函式
以下是Py_BuildValue 函式的標準簽名:
PyObject* Py_BuildValue(char* format,...)
這裡的 format 是一個 C 字串,描述了要構建的 Python 物件。Py_BuildValue 的以下引數是構建結果的 C 值。PyObject* 結果是一個新引用。
下表列出了常用的程式碼字串,其中零個或多個程式碼字串連線成字串格式。
程式碼 | C 型別 | 含義 |
---|---|---|
c | char | C char 變為長度為 1 的 Python 字串。 |
d | double | C double 變為 Python float。 |
f | float | C float 變為 Python float。 |
i | int | C int 變為 Python int |
l | long | C long 變為 Python int |
N | PyObject* | 傳遞 Python 物件並竊取引用。 |
O | PyObject* | 傳遞 Python 物件並按正常方式 INCREF。 |
O& | convert+void* | 任意轉換 |
s | char* | C 以 0 結尾的 char* 到 Python 字串,或 NULL 到 None。 |
s# | char*+int | C char* 和長度到 Python 字串,或 NULL 到 None。 |
u | Py_UNICODE* | C 寬的、以 null 結尾的字串到 Python Unicode,或 NULL 到 None。 |
u# | Py_UNICODE*+int | C 寬字串和長度到 Python Unicode,或 NULL 到 None。 |
w# | char*+int | 讀/寫單段緩衝區到 C 地址和長度。 |
z | char* | 類似於 s,也接受 None(將 C char* 設定為 NULL)。 |
z# | char*+int | 類似於 s#,也接受 None(將 C char* 設定為 NULL)。 |
(...) | 根據 ... | 從 C 值構建 Python 元組。 |
[...] | 根據 ... | 從 C 值構建 Python 列表。 |
{...} | 根據 ... | 從 C 值構建 Python 字典,交替鍵和值。 |
程式碼 {...} 從偶數個 C 值構建字典,交替鍵和值。例如,Py_BuildValue("{issi}",23,"zig","zag",42) 返回類似於 Python 的 {23:'zig','zag':42} 的字典