Categories: Python

Python 以 ctypes 載入 DLL、SO 動態連結函式庫教學與範例

介紹如何在 Python 中使用 ctypes 載入 DLL 與 SO 動態連結函式庫,搭配 C/C++ 語言進行高速計算。

Python 的 ctypes 模組可以用來呼叫外部的函式庫,提供相容於 C 語言的資料型別,開發者可以透過這個模組直接呼叫 DLL 檔案(Windows 平台)或 SO 檔案(macOS 或 Linux 平台)中的函數。以下分別介紹 Windows 平台建立與使用 DLL 檔案、macOS 與 Linux 平台建立與使用 SO 檔案的步驟。

Windows 平台(DLL 檔案)

在 Windows 平台中可將 C/C++ 語言的程式編譯成 DLL 動態連結函式庫,再以 Python 的 ctypes 載入使用。

建立 DLL 動態連結函式庫

若在 Windows 中要建立 DLL 動態連結函式庫,可以使用 Visual Studio 開發環境來建立 DLL 檔案,這部分可以參考 Visual Studio 2019 建立 DLL 動態連結函式庫教學與範例,而在編譯時要注意編譯的版本(32 位元或 64 位元)要跟 Python 的版本相同。

Python 使用 DLL 動態連結函式庫

準備好 MyDLL.dll 這個動態連結函式庫檔案之後,就可以在 Python 中利用 ctypes 載入並呼叫其中的函數了。

from ctypes import cdll, c_double

# 載入 DLL 動態連結函式庫
mydll = cdll.LoadLibrary('./MyDLL.dll')

# 設定 DLL 檔案中 dist 函數的參數資料型態
mydll.dist.argtypes = [c_double, c_double, c_double, c_double]

# 設定 DLL 檔案中 dist 函數的傳回值資料型態
mydll.dist.restype = c_double

# 呼叫 DLL 檔案中的 dist 函數
d = mydll.dist(1.0, 1.0, 4.0, 5.0)
print("dist =", d)
dist = 5.0

macOS 與 Linux 平台(DLL 檔案)

若在 macOS 與 Linux 平台中,則會改用 gccg++ 將 C/C++ 的程式編譯成 SO 動態連結檔,再以 Python 的 ctypes 載入使用。

建立 SO 動態連結檔案

以 C++ 撰寫一個計算歐氏距離(Euclidean Distance)的函數,原始碼如下:

#include <cmath>

// 計算歐氏距離函數
extern "C" double dist(
  const double x1, const double y1,
  const double x2, const double y2) {
  return std::sqrt(std::pow(x1 - x2, 2) + std::pow(y1 - y2, 2));
}

將這段 C++ 程式碼儲存為 my_so.cpp,並以 g++ 將其編譯成 SO 動態連結檔案:

# 將 C++ 程式編譯成 SO 動態連結檔案
g++ -Wall -Wextra -O -ansi -pedantic -fPIC -shared my_so.cpp -o my_so.so

編譯成功之後,就會產生 my_so.so 這個動態連結檔案。

Python 使用 DLL 動態連結函式庫

準備好 my_so.so 這個動態連結檔案之後,就可以在 Python 中利用 ctypes 載入並呼叫其中的函數了。

from ctypes import cdll, c_double

# 載入 SO 動態連結檔案
mydll = cdll.LoadLibrary('./my_so.so')

# 設定 SO 檔案中 dist 函數的參數資料型態
mydll.dist.argtypes = [c_double, c_double, c_double, c_double]

# 設定 SO 檔案中 dist 函數的傳回值資料型態
mydll.dist.restype = c_double

# 呼叫 SO 檔案中的 dist 函數
d = mydll.dist(1.0, 1.0, 4.0, 5.0)
print("dist =", d)
dist = 5.0

傳遞陣列

若需要在 Python 與 C/C++ 之間傳遞整個陣列的資料,可以使用指標的方式,以下是一個簡單的 SO 動態連結檔案範例。

假設 C++ 的程式碼如下,sort 函數會將傳入的陣列進行排序:

#include <algorithm>
#include <functional>

// 排序函數
extern "C" void sort(
  double array[], const unsigned int size) {
  std::sort(array, array + size, std::less<double>());
}

將這段 C++ 程式碼編譯成 my_so2.so 動態連結檔案之後,在 Python 中使用以下方式傳入 NumPy 陣列即可進行資料的排序。

import numpy as np
from ctypes import cdll, c_double, c_uint, POINTER

# 載入 SO 動態連結檔案
mydll = cdll.LoadLibrary('./my_so2.so')

# 設定 SO 檔案中 sort 函數的參數資料型態
mydll.sort.argtypes = [POINTER(c_double), c_uint]

# 設定 SO 檔案中 sort 函數的傳回值資料型態
mydll.sort.restype = None

# 建立 NumPy 資料陣列
data = np.array([3.2, 5.6, 1.2, 9.4, 7.3])

# 取得陣列的指標
dataPtr = data.ctypes.data_as(POINTER(c_double))

# 呼叫 SO 檔案中的 sort 函數
mydll.sort(dataPtr, data.size)
print(data)
[1.2 3.2 5.6 7.3 9.4]

參考資料:Wolfprojects

Share
Published by
Office Guide

Recent Posts

Python 使用 PyAutoGUI 自動操作滑鼠與鍵盤

本篇介紹如何在 Python ...

9 個月 ago

Ubuntu Linux 以 WireGuard 架設 VPN 伺服器教學與範例

本篇介紹如何在 Ubuntu ...

9 個月 ago

Linux 網路設定 ip 指令用法教學與範例

本篇介紹如何在 Linux 系...

9 個月 ago

Windows 使用 TPM 虛擬智慧卡保護 SSH 金鑰教學與範例

本篇介紹如何在 Windows...

10 個月 ago

Linux 以 Shamir’s Secret Sharing 分割保存金鑰教學與範例

介紹如何在 Linux 中使用...

11 個月 ago

Linux 以 Cryptsetup、LUKS 加密 USB 隨身碟教學與範例

介紹如何在 Linux 系統中...

11 個月 ago