C語(yǔ)言中調(diào)用Python腳本的方法詳解
在 C 語(yǔ)言中調(diào)用 Python 腳本的方式有很多,在這里使用 Python 的 C API 進(jìn)行調(diào)用 Python 腳本,包括初始化解釋器、執(zhí)行腳本文件(導(dǎo)入模塊和類、創(chuàng)建對(duì)象、調(diào)用對(duì)象方法)以及處理錯(cuò)誤的完整流程。 參考 Python 的 C API 文檔: Python/C API 參考手冊(cè)
示例代碼:python_c_api_demo
環(huán)境準(zhǔn)備
- 示例使用的是 Python3.9 ,需要安裝 python 3.9 以及 python3-dev
- 安裝 gcc
- 執(zhí)行環(huán)境必須有 Python 環(huán)境
簡(jiǎn)單調(diào)用示例
單線程導(dǎo)入模塊以及類,創(chuàng)建對(duì)象,調(diào)用對(duì)象方法。
Python 腳本
example.py
import random
import time
class HelloWorld:
def __init__(self, name):
self.name = name
def say_hi(self):
print(f'Hi {self.name}')
def add(self, a, b):
return a + b
def random_number(self):
time.sleep(0.05)
return random.randint(0, 100)
C 語(yǔ)言代碼
直接在 example.c 中執(zhí)行 Python 腳本。
#include <Python.h>
int main()
{
// 初始化 Python 解釋器
Py_Initialize();
// 添加當(dāng)前目錄到 sys.path(確保能導(dǎo)入 example.py)
PyObject *pSys = PyImport_ImportModule("sys");
if (!pSys)
{
PyErr_Print();
fprintf(stderr, "Failed to import sys module\n");
return 1;
}
PyObject *pPath = PyObject_GetAttrString(pSys, "path");
if (!pPath)
{
PyErr_Print();
fprintf(stderr, "Failed to get sys.path\n");
Py_DECREF(pSys);
return 1;
}
int status = PyList_Append(pPath, PyUnicode_FromString("."));
if (status == -1)
{
PyErr_Print();
fprintf(stderr, "Failed to append path\n");
Py_DECREF(pPath);
Py_DECREF(pSys);
return 1;
}
Py_DECREF(pPath);
Py_DECREF(pSys);
// 導(dǎo)入 example 模塊
PyObject *pModule = PyImport_ImportModule("example");
if (!pModule)
{
PyErr_Print();
fprintf(stderr, "Failed to import example module\n");
Py_Finalize();
return 1;
}
// 導(dǎo)入 HelloWorld 類
PyObject *pClass = PyObject_GetAttrString(pModule, "HelloWorld");
if (!pClass)
{
PyErr_Print();
fprintf(stderr, "Failed to import HelloWorld class\n");
Py_DECREF(pModule);
Py_Finalize();
return 1;
}
// 創(chuàng)建對(duì)象
char name[256] = "XiaoMing";
PyObject *pArgs = PyTuple_New(1);
PyTuple_SetItem(pArgs, 0, PyUnicode_FromString(name));
PyObject *pInstance = PyObject_CallObject(pClass, pArgs);
Py_DECREF(pArgs);
if (pInstance == NULL)
{
PyErr_Print();
fprintf(stderr, "Failed to create instance of HelloWorld\n");
Py_XDECREF(pClass);
Py_XDECREF(pModule);
return 1;
}
// 獲取 say_hi 方法
PyObject *pFuncSayHi = PyObject_GetAttrString(pInstance, "say_hi");
if (!pFuncSayHi || !PyCallable_Check(pFuncSayHi))
{
PyErr_Print();
fprintf(stderr, "Failed to get say_hi function\n");
Py_DECREF(pInstance);
Py_DECREF(pClass);
Py_DECREF(pModule);
Py_Finalize();
return 1;
}
// 調(diào)用 say_hi 方法
PyObject_CallObject(pFuncSayHi, NULL);
// 獲取 add 函數(shù)
PyObject *pFuncAdd = PyObject_GetAttrString(pInstance, "add");
if (!pFuncAdd || !PyCallable_Check(pFuncAdd))
{
PyErr_Print();
fprintf(stderr, "Failed to get add function\n");
Py_DECREF(pFuncSayHi);
Py_DECREF(pInstance);
Py_DECREF(pClass);
Py_DECREF(pModule);
Py_Finalize();
return 1;
}
// 構(gòu)造參數(shù)并調(diào)用 add 函數(shù)
PyObject *pArgsAdd = PyTuple_Pack(2, PyLong_FromLong(3), PyLong_FromLong(4));
if (!pArgsAdd)
{
PyErr_Print();
Py_DECREF(pFuncAdd);
Py_DECREF(pFuncSayHi);
Py_DECREF(pInstance);
Py_DECREF(pClass);
Py_DECREF(pModule);
Py_Finalize();
return 1;
}
PyObject *pResultAdd = PyObject_CallObject(pFuncAdd, pArgsAdd);
if (!pResultAdd)
{
PyErr_Print();
fprintf(stderr, "Failed to call add function\n");
Py_DECREF(pArgsAdd);
Py_DECREF(pFuncAdd);
Py_DECREF(pFuncSayHi);
Py_DECREF(pInstance);
Py_DECREF(pClass);
Py_DECREF(pModule);
Py_Finalize();
return 1;
}
// 處理返回值
long addResult = PyLong_AsLong(pResultAdd);
printf("Add Result: %ld\n", addResult);
// 獲取 random_number 方法
PyObject *pFuncRandomNumber = PyObject_GetAttrString(pInstance, "random_number");
if (!pFuncRandomNumber || !PyCallable_Check(pFuncRandomNumber))
{
PyErr_Print();
fprintf(stderr, "Failed to get random_number function\n");
Py_DECREF(pResultAdd);
Py_DECREF(pArgsAdd);
Py_DECREF(pFuncAdd);
Py_DECREF(pFuncSayHi);
Py_DECREF(pInstance);
Py_DECREF(pClass);
Py_DECREF(pModule);
Py_Finalize();
return 1;
}
// 調(diào)用 random_number 方法
PyObject *pRandomNumber = PyObject_CallObject(pFuncRandomNumber, NULL);
long randomNumber = PyLong_AsLong(pRandomNumber);
printf("Random Number: %ld\n", randomNumber);
// 釋放資源
Py_DECREF(pRandomNumber);
Py_DECREF(pFuncRandomNumber);
Py_DECREF(pResultAdd);
Py_DECREF(pArgsAdd);
Py_DECREF(pFuncAdd);
Py_DECREF(pFuncSayHi);
Py_DECREF(pInstance);
Py_DECREF(pClass);
Py_DECREF(pModule);
// 結(jié)束 Python 解釋器
Py_Finalize();
return 0;
}編譯命令:gcc example.c -I/usr/include/python3.9 -lpython3.9 -o example
多線程調(diào)用示例
當(dāng)在 C 程序中創(chuàng)建多線程調(diào)用 Python API 時(shí),必須注意 GIL 的獲取和釋放,使用 PyGILState_Ensure() 和 PyGILState_Release() 來(lái)獲取和釋放 GIL。 在主線程使用Py_Initialize初始化 Python 解釋器后,主線程會(huì)自動(dòng)持有 GIL,必須顯式釋放 GIL,否則子線程中會(huì)一直阻塞在獲取 GIL
example_threading.c
在主線程中初始化 Python 解釋器,并導(dǎo)入模塊和類,創(chuàng)建多個(gè)線程,每個(gè)線程都創(chuàng)建對(duì)象,并調(diào)用對(duì)象方法。
#include <Python.h>
#define NUM_THREADS 5
#define NUM_ITERATIONS 50
// 線程參數(shù)結(jié)構(gòu)
typedef struct
{
int thread_id;
const char *name;
PyObject *pClass;
} ThreadArgs;
// 生成隨機(jī)字符串的函數(shù)
void generate_random_string(char *buffer, int length, const char *charset)
{
int charset_len = strlen(charset);
for (int i = 0; i < length; ++i)
{
int index = rand() % charset_len; // 從字符集中隨機(jī)選一個(gè)字符
buffer[i] = charset[index];
}
}
// 線程函數(shù)
void *thread_func(void *args)
{
ThreadArgs *targs = (ThreadArgs *)args;
int tid = targs->thread_id;
printf("Thread %d started\n", tid);
PyGILState_STATE gstate;
gstate = PyGILState_Ensure();
PyObject *pArgs = PyTuple_New(1);
PyTuple_SetItem(pArgs, 0, PyUnicode_FromString(targs->name));
PyGILState_Release(gstate);
gstate = PyGILState_Ensure();
PyObject *pInstance = PyObject_CallObject(targs->pClass, pArgs);
Py_DECREF(pArgs);
if (pInstance == NULL)
{
PyErr_Print();
fprintf(stderr, "Failed to create instance of HelloWorld\n");
PyGILState_Release(gstate);
return NULL;
}
PyGILState_Release(gstate);
// 獲取 say_hi 方法
gstate = PyGILState_Ensure();
PyObject *pFuncSayHi = PyObject_GetAttrString(pInstance, "say_hi");
if (!pFuncSayHi || !PyCallable_Check(pFuncSayHi))
{
PyErr_Print();
fprintf(stderr, "Failed to get say_hi function\n");
Py_DECREF(pInstance);
PyGILState_Release(gstate);
Py_Finalize();
return NULL;
}
// 調(diào)用 say_hi 方法
PyObject_CallObject(pFuncSayHi, NULL);
PyGILState_Release(gstate);
// 獲取 add 函數(shù)
gstate = PyGILState_Ensure();
PyObject *pFuncAdd = PyObject_GetAttrString(pInstance, "add");
if (!pFuncAdd || !PyCallable_Check(pFuncAdd))
{
PyErr_Print();
fprintf(stderr, "Failed to get add function\n");
Py_DECREF(pFuncSayHi);
Py_DECREF(pInstance);
PyGILState_Release(gstate);
Py_Finalize();
return NULL;
}
// 構(gòu)造參數(shù)并調(diào)用 add 函數(shù)
PyObject *pArgsAdd = PyTuple_Pack(2, PyLong_FromLong(3), PyLong_FromLong(4));
if (!pArgsAdd)
{
PyErr_Print();
Py_DECREF(pFuncAdd);
Py_DECREF(pFuncSayHi);
Py_DECREF(pInstance);
PyGILState_Release(gstate);
Py_Finalize();
return NULL;
}
PyObject *pResultAdd = PyObject_CallObject(pFuncAdd, pArgsAdd);
if (!pResultAdd)
{
PyErr_Print();
fprintf(stderr, "Failed to call add function\n");
Py_DECREF(pArgsAdd);
Py_DECREF(pFuncAdd);
Py_DECREF(pFuncSayHi);
Py_DECREF(pInstance);
PyGILState_Release(gstate);
Py_Finalize();
return NULL;
}
// 處理返回值
long addResult = PyLong_AsLong(pResultAdd);
PyGILState_Release(gstate);
printf("Add Result: %ld\n", addResult);
// 獲取 random_number 方法
gstate = PyGILState_Ensure();
PyObject *pFuncRandomNumber = PyObject_GetAttrString(pInstance, "random_number");
if (!pFuncRandomNumber || !PyCallable_Check(pFuncRandomNumber))
{
PyErr_Print();
fprintf(stderr, "Failed to get random_number function\n");
Py_DECREF(pResultAdd);
Py_DECREF(pArgsAdd);
Py_DECREF(pFuncAdd);
Py_DECREF(pFuncSayHi);
Py_DECREF(pInstance);
PyGILState_Release(gstate);
Py_Finalize();
return NULL;
}
PyGILState_Release(gstate);
srand(time(NULL) + tid);
// 循環(huán)多次調(diào)用 random_number 方法
for (int i = 0; i < NUM_ITERATIONS; i++)
{
gstate = PyGILState_Ensure();
PyObject *pRandomNumber = PyObject_CallObject(pFuncRandomNumber, NULL);
long randomNumber = PyLong_AsLong(pRandomNumber);
printf("Thread %d, Iteration %d, Random Number: %ld\n", tid, i, randomNumber);
Py_DECREF(pRandomNumber);
PyGILState_Release(gstate);
usleep(10000);
}
// 釋放資源
gstate = PyGILState_Ensure();
Py_DECREF(pFuncRandomNumber);
Py_DECREF(pResultAdd);
Py_DECREF(pArgsAdd);
Py_DECREF(pFuncAdd);
Py_DECREF(pFuncSayHi);
Py_DECREF(pInstance);
PyGILState_Release(gstate);
printf("Thread %d end\n", tid);
return NULL;
}
int main()
{
// 主線程初始化一次 Python 解釋器
Py_Initialize();
// 添加當(dāng)前目錄到 sys.path(確保能導(dǎo)入 example.py)
PyObject *pSys = PyImport_ImportModule("sys");
if (!pSys)
{
PyErr_Print();
fprintf(stderr, "Failed to import sys module\n");
return 1;
}
PyObject *pPath = PyObject_GetAttrString(pSys, "path");
if (!pPath)
{
PyErr_Print();
fprintf(stderr, "Failed to get sys.path\n");
Py_DECREF(pSys);
return 1;
}
int status = PyList_Append(pPath, PyUnicode_FromString("."));
if (status == -1)
{
PyErr_Print();
fprintf(stderr, "Failed to append path\n");
Py_DECREF(pPath);
Py_DECREF(pSys);
return 1;
}
Py_DECREF(pPath);
Py_DECREF(pSys);
// 導(dǎo)入 example 模塊
PyObject *pModule = PyImport_ImportModule("example");
if (!pModule)
{
PyErr_Print();
fprintf(stderr, "Failed to import example module\n");
Py_Finalize();
return 1;
}
// 導(dǎo)入 HelloWorld 類
PyObject *pClass = PyObject_GetAttrString(pModule, "HelloWorld");
if (!pClass)
{
PyErr_Print();
fprintf(stderr, "Failed to import HelloWorld class\n");
Py_DECREF(pModule);
Py_Finalize();
return 1;
}
PyEval_SaveThread(); // 釋放 GIL
srand(time(NULL)); // 初始化隨機(jī)種子
const char *charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
// 創(chuàng)建線程
pthread_t threads[NUM_THREADS];
ThreadArgs thread_args[NUM_THREADS];
for (int i = 0; i < NUM_THREADS; ++i)
{
printf("Create thread %d\n", i);
char buffer[5]; // 長(zhǎng)度為 5 個(gè)結(jié)束符
generate_random_string(buffer, 5, charset);
thread_args[i].thread_id = i;
thread_args[i].name = buffer;
thread_args[i].pClass = pClass;
int rc = pthread_create(&threads[i], NULL, thread_func, &thread_args[i]);
if (rc)
{
fprintf(stderr, "Error creating thread %d\n", i);
return EXIT_FAILURE;
}
// usleep(100000);
}
printf("create threads success\n");
for (int i = 0; i < NUM_THREADS; ++i)
{
pthread_join(threads[i], NULL);
}
PyGILState_STATE gstate = PyGILState_Ensure();
Py_DECREF(pClass);
Py_DECREF(pModule);
// 結(jié)束 Python 解釋器
Py_Finalize();
return 0;
}編譯命令:gcc example_threading.c -I/usr/include/python3.9 -lpython3.9 -lpthread -o example_threading
跨平臺(tái)編譯
在 x64 上編譯到 arm64 平臺(tái)的可執(zhí)行文件,需要將目標(biāo)設(shè)備上的 python 相關(guān)頭文件和庫(kù)文件復(fù)制到編譯機(jī)上,并使用 aarch64-linux-gnu-gcc 編譯。
編譯命令:aarch64-linux-gnu-gcc example_threading.c -o example_threading_arm64 -I./arm64-python3.12/include/python3.12 -L./arm64-python3.12/lib -lpython3.12 -lpthread -lm -lutil -ldl -Wl,-rpath,.
問(wèn)題
在 Python 腳本中使用 Numpy 時(shí),出現(xiàn)報(bào)錯(cuò):numpy:DLL load failed while importing _multiarray_umath:,如果 Python 庫(kù)中使用了 C 擴(kuò)展,應(yīng)該都會(huì)有這個(gè)問(wèn)題,解決方案:在初始化 python 解釋器前,動(dòng)態(tài)加載 python 的 so 庫(kù)
#include <dlfcn.h>
dlopen("libpython3.9.so", RTLD_LAZY | RTLD_GLOBAL)
以上就是C語(yǔ)言中調(diào)用Python腳本的方法詳解的詳細(xì)內(nèi)容,更多關(guān)于C語(yǔ)言調(diào)用Python腳本的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
QT編寫(xiě)簡(jiǎn)單登錄界面的實(shí)現(xiàn)示例
登陸界面是網(wǎng)頁(yè)中常見(jiàn)的界面,本文主要介紹了QT編寫(xiě)簡(jiǎn)單登錄界面的實(shí)現(xiàn)示例,具有一定的參考價(jià)值,感興趣的可以了解一下2024-02-02
利用Debug調(diào)試代碼解決0xC0000005:?讀取位置?0x0000000000000000?時(shí)發(fā)生訪問(wèn)沖突問(wèn)
這篇文章主要介紹了利用Debug調(diào)試代碼解決0xC0000005:?讀取位置?0x0000000000000000?時(shí)發(fā)生訪問(wèn)沖突,本文給大家分享完美解決方案,需要的朋友可以參考下2023-03-03
C++調(diào)用EasyX庫(kù)實(shí)現(xiàn)嫦娥奔月小游戲
這篇文章主要為大家詳細(xì)介紹了C++如何調(diào)用EasyX庫(kù)編寫(xiě)一個(gè)簡(jiǎn)單的嫦娥奔月小游戲,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以參考一下2023-09-09
C語(yǔ)言實(shí)現(xiàn)十六進(jìn)制與二進(jìn)制的相互轉(zhuǎn)換
這篇文章主要為大家詳細(xì)介紹了如何利用c語(yǔ)言實(shí)現(xiàn)將文件中十六進(jìn)制數(shù)據(jù)與二進(jìn)制數(shù)據(jù)相互轉(zhuǎn)換,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,感興趣的可以學(xué)習(xí)一下2022-11-11
C語(yǔ)言動(dòng)態(tài)內(nèi)存分配圖文講解
給數(shù)組分配多大的空間?你是否和初學(xué)C時(shí)的我一樣,有過(guò)這樣的疑問(wèn)。這一期就來(lái)聊一聊動(dòng)態(tài)內(nèi)存的分配,讀完這篇文章,你可能對(duì)內(nèi)存的分配有一個(gè)更好的理解2023-01-01
C語(yǔ)言入門(mén)篇--注釋,關(guān)鍵字typedef及轉(zhuǎn)義字符詳解
本篇文章是c語(yǔ)言基礎(chǔ)篇,主要為大家介紹了C語(yǔ)言的關(guān)鍵字typedef,注釋,轉(zhuǎn)義字符的基本理論知識(shí),希望可以幫助大家快速入門(mén)c語(yǔ)言的世界,更好的理解c語(yǔ)言2021-08-08
Mac OS X 10.8 中編譯APUE(Unix環(huán)境高級(jí)編程)的源代碼過(guò)程
這篇文章主要介紹了Mac OS X 10.8 中編譯APUE(Unix環(huán)境高級(jí)編程)的源代碼過(guò)程,對(duì)于用MAC學(xué)習(xí)Unix環(huán)境高級(jí)編程的同學(xué)會(huì)有些作用,需要的朋友可以參考下2014-09-09

