ctypes python_Python ctypes 使用总结
Python 調用 C/C++ 的程序主要有兩種方式:
使用 ctypes 調用動態庫
ctypes 的方式相對來說成本較低,首先 ctypes 是內置庫,使用方便,使用的過程中與 C/C++ 動態庫的邏輯是完全獨立的,互相可以單獨維護。但是相對也有明顯的缺點,C++ 在編譯后的函數名會變,ctypes 使用起來不方便,API 相對也比較繁瑣,寫起來略微麻煩一些,使用出錯的話會導致進程退出。
本文主要介紹 ctypes 的基本用法,可以對現有的 C/C++ 代碼進行簡單的二次封裝后進行使用。筆者認為 ctypes 本身還是比較適合輕量級的使用場景,如果邏輯較為復雜的,請考慮使用 C/C++ 擴展的方式。
本文的 Python 以 2.7 版本為例,編譯使用 gcc。
簡單示例
寫一個簡單的 Hello 來說明一下最基本的用法。首先定義一個簡單的 c 函數。
//hello_module.c
#include
int hello(const char* name) {
printf("hello %s!\n", name);
return 0;
}
編譯生成動態庫,動態庫不同的系統后綴不同(Windows 的 dll,Linux 的 so,Mac 的 dylib),需要注意,本文以 so 為例。
gcc -fPIC -shared hello_module.c -o hello_module.so
通過 ctypes 來進行動態庫加載及函數調用,注意 windows 的調用方式有專有的 API。
import ctypes
lib = ctypes.cdll.LoadLibrary("hello_module.so")
lib.hello("world") # hello world!
以上便是簡單的 ctypes 使用流程,加載動態庫,然后就可以調用動態庫中的函數。
有幾點需要注意的地方:
類型的隱私轉換的,python 的 str 轉換為了 c 的 const char*
默認的函數返回值認為是 int,不為 int 的需要自行修改
函數的參數類型未指定,只能使用 ctypes 自帶的類型隱私轉換
基礎類型可以參考官方文檔的對應表格,需要額外說明的一點是,int 和 uint 都有對應的 8、16、32、64 的類型可供使用。
數組和指針類型
基本類型中只包含了 c_char_p 和 c_void_p 兩個指針類型,其他的指針類型該如何使用?數組該如何定義和使用?我們來看看這兩個類型的使用。
編寫一個為數組求和的函數。
//sum_module.c
#include
int sum(int a[], size_t len) {
int ret = 0;
for (size_t i = 0; i < len; i++) {
ret += a[i];
}
return ret;
}
int sum2(int* a, size_t len) {
int ret = 0;
for (size_t i = 0; i < len; i++) {
ret += a[i];
}
return ret;
}
和上面一樣進行編譯,生成動態庫
gcc -fPIC -shared sum_module.c -o sum_module.so
像之前一樣來使用,看下會怎樣。
import ctypes
lib = ctypes.cdll.LoadLibrary("sum_module.so")
lib.sum([1, 2, 3], 3)
#Traceback (most recent call last):
# File "demo.py", line 7, in
# lib.sum([1, 2, 3], 3)
#ctypes.ArgumentError: argument 1: : Don't know how to convert parameter 1
會發現 ctypes 報錯了,不知道類型如何進行轉換,也就是說 ctypes 的隱式轉換是不支持數組類型的。
我們需要用 ctypes 的數組來傳參數。
import ctypes
lib = ctypes.cdll.LoadLibrary("sum_module.so")
array = (ctypes.c_int * 3)(1, 2, 3)
print lib.sum(array, len(array))
i = ctypes.c_int(5)
print lib.sum(i, 1)
ctypes 的數組定義就是用 ctypes 中的類型 * 大小。
下面我們看一下指針的用法。
import ctypes
lib = ctypes.cdll.LoadLibrary("sum_module.so")
i = ctypes.c_int(5)
lib.sum2.argtypes = (ctypes.POINTER(ctypes.c_int), ctypes.c_size_t)
print lib.sum2(ctypes.pointer(i), 1)
POINTER 是用來定義指針類型,pointer 用來獲取一個變量的指針,相當于 C 里面的 &。
pointer 的用法需要注意的是,必須在 ctypes 類型上使用,不能在 python 類型上使用。
import ctypes
i = ctypes.c_int(5)
print ctypes.pointer(i) # <__main__.LP_c_int object at 0x10566f7a0>
i = 5
print ctypes.pointer(i) # TypeError: _type_ must have storage info
這就是數組和指針的基本使用方式,注意指針和數組的區分,這里定義的 sum 和 sum2 只是舉例,sum2 第一個參數也可以接受數組,這個和在 C 里面是一樣的。
函數參數類型和返回值類型
之前的例子只有一個明確指定了參數類型,沒有指定返回類型。返回類型默認是 int,如果需要返回非 int 的類型就需要進行指定。
指定參數類型的好處在于,ctypes 可以處理指針的轉換,無需代碼中進行轉換。
繼續使用上一個 sum2 函數為例。
i = ctypes.c_int(5)
lib.sum2.argtypes = (ctypes.POINTER(ctypes.c_int), ctypes.c_size_t)
print lib.sum2(ctypes.pointer(i), 1)
print lib.sum2(i, 1)
可以使用 pointer(i) 和 i 作為 sum2 的第一個參數,會自動處理是否為指針的情況。
結構體
結構體在 ctypes 需要進行類的定義,類型和指針的使用方式和之前一致。
下面我們看一個 struct 定義的實例。
import ctypes
"""
typedef struct _user {
int type;
uint64_t userid;
char username[64];
unsigned int created_at;
} user;
"""
class User(ctypes.Structure):
_fields_ = [
('type', ctypes.c_int),
('userid', ctypes.c_uint64),
('username', ctypes.c_char * 64),
('created_at', ctypes.c_uint),
]
print ctypes.POINTER(User) # u = User()
print ctypes.pointer(u) # <__main__.LP_User object at 0x10982c7a0>
總結
ctypes 也支持聯合體 union,但是因為不常用,所以本文沒有提及。有需要的可以參考官方文檔。
參考資料
總結
以上是生活随笔為你收集整理的ctypes python_Python ctypes 使用总结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 用python绘制好看的图形_如何使用P
- 下一篇: 一个用python做的完整项目_我从一个