Python進階篇-ctypes(上)

在Windows平臺上,把一些常用的算法或者功能封裝成庫是非常常見的,在開發python程序的時候,我們可能會用到這功能,把C/C++代碼轉化爲python代碼可能是一個非常出力不討好的事情,這時候python調用C就會體現出巨大的優勢(不過多考慮運行效率的情況下)。Python調C的核心就在於數據類型的轉換,這裏和大家一起探討一下ctypes的用法。
https://docs.python.org/zh-cn/3.7/library/ctypes.html
作爲準備請讀一讀官方的文檔。
對於簡單的數據類型,這裏就不過多介紹了,請參考官方文檔。
接下來,我們重點看一下複雜的數據結構:結構體。
首先,我們來編寫一個Windows平臺下的動態庫。

typedef struct _Coordinate {
	int x;
	int y;
}Coordinate, * PCoordinate;

int add(Coordinate pt)
{
	int result = 0;
	result = pt.x + pt.y;
	return result;
}

接下來我們,來編寫python代碼來調用dll,代碼如下:

import ctypes
from ctypes import *


class Coordinate(Structure):
    _fields_ = [
        ('x', ctypes.c_int),
        ('y', ctypes.c_int)
    ]


def main():
    coord = Coordinate(2, 3)
    mydll = ctypes.WinDLL("path/to/dll")
    result = mydll.add(coord)
    print(result)


if __name__ == "__main__":
    main()

這個例子應該很好懂,接下來升級一下這個問題,結構體嵌套
修改我們的dll 庫,代碼如下

typedef struct _Coordinate {
	int x;
	int y;
}Coordinate, * PCoordinate;


typedef struct _Point {
	Coordinate coordinate;
}Point, *PPoint;


int add(Point pt)
{
	int result = 0;
	result = pt.coordinate.x + pt.coordinate.y;
	return result;
}

修改python 代碼如下:

import ctypes
from ctypes import *


class Coordinate(Structure):
    _fields_ = [
        ('x', ctypes.c_int),
        ('y', ctypes.c_int)
    ]


class Point(Structure):
    _fields_ = [
        ('coordinate', Coordinate)
    ]

def main():
    coord = Coordinate(2, 3)
    pt = Point(coord)
    mydll = ctypes.WinDLL("path/to/dll")
    result = mydll.add(pt)
    print(result)


if __name__ == "__main__":
    main()

進一步升級這個問題,傳結構體指針給dll
修改dll 代碼如下:

typedef struct _Coordinate {
	int x;
	int y;
}Coordinate, * PCoordinate;


typedef struct _Point {
	Coordinate coordinate;
}Point, *PPoint;


int add(PPoint pt)
{
	int result = 0;
	result = pt->coordinate.x + pt->coordinate.y;
	return result;
}

修改python代碼如下:

import ctypes
from ctypes import *


class Coordinate(Structure):
    _fields_ = [
        ('x', ctypes.c_int),
        ('y', ctypes.c_int)
    ]


class Point(Structure):
    _fields_ = [
        ('coordinate', Coordinate)
    ]

def main():
    coord = Coordinate(2, 3)
    pt = Point(coord)
    mydll = ctypes.WinDLL("D:\\Code\\C\\Test\\Test\\x64\\Debug\\Test.dll")
    result = mydll.add(ctypes.byref(pt))
    print(result)


if __name__ == "__main__":
    main()

讓我們來進一步升級這個問題, 在結構體內定義,指針變量。
修改dll代碼如下:

typedef struct _Coordinate {
	int x;
	int y;
}Coordinate, * PCoordinate;


typedef struct _Point {
	PCoordinate coordinate;
}Point, *PPoint;


int add(PPoint pt)
{
	int result = 0;
	result = pt->coordinate->x + pt->coordinate->y;
	return result;
}

修改python代碼如下:

import ctypes
from ctypes import *


class Coordinate(Structure):
    _fields_ = [
        ('x', ctypes.c_int),
        ('y', ctypes.c_int)
    ]


class Point(Structure):
    _fields_ = [
        ('coordinate', POINTER(Coordinate))
    ]


def main():
    coord = Coordinate(2, 3)
    pt = Point(pointer(coord))

    mydll = ctypes.WinDLL("D:\\Code\\C\\Test\\Test\\x64\\Debug\\Test.dll")
    res = mydll.add(ctypes.byref(pt))
    print(res)


if __name__ == "__main__":
    main()

最後我們再來聊一下,POINTER, byref和pointer三者的區別。
POINTER:接受ctypes類型返回新的類型,類似 T*
pointer:返回的是一個具體的實例對象
關於byref官方的解釋如下:
“ctypes exports the byref() function which is used to pass parameters by reference. The same effect can be achieved with the pointer() function, although pointer() does a lot more work since it constructs a real pointer object, so it is faster to use byref() if you don’t need the pointer object in Python itself:”
大概就是pointer會返回對象而byref不會,所以byref更快,另外byref只在傳參的時候使用。

接下來還有數組,字節序,以及位域相關的東西,下一篇來寫。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章