1.最简单的调用方式:
int alc_FFT_of(const char *topic_fmt, int ch, cJSON *result)
{
//Py_Initialize();
pthread_mutex_lock(&mqtt_mutex);
// 调用 Python 函数
PyRun_SimpleString("print('Hello, World!')");
pthread_mutex_unlock(&mqtt_mutex);
//Py_Finalize();
return 0;
}
注意,上面的是最简单的单向执行代码。代码如果涉及多线程执行,可以使用全局mutex来进行同步。
2.数据传递
2.1 方法一,文本模式,通过字典对象:
//在Python解释器中获取脚本的主模块和全局字典:
PyObject *pModule = NULL;
PyObject *pDict = NULL;
pModule = PyImport_AddModule("__main__");
pDict = PyModule_GetDict(pModule);
//使用PyRun_String函数来执行Python代码,并传递参数给脚本。可以使用PyEval_GetGlobals()函数获取脚本执行的全局字典,在这个字典中你可以定义脚本中可用的变量或者函数。
PyObject *pResult = NULL;
const char *script = "print('Hello, ' + name)";
PyObject *pGlobals = PyEval_GetGlobals();
PyDict_SetItemString(pGlobals, "name", PyUnicode_FromString("John"));
pResult = PyRun_String(script, Py_file_input, pDict, pGlobals);
//在上述例子中,我们将name变量传递给Python脚本。你可以根据需要传递更多的参数,只需在全局字典中设置相应的变量即可。
//在获取结果之后,你可以根据需要处理脚本的输出或返回值(如果有的话):
if (pResult != NULL) {
if (PyErr_Occurred()) {
PyErr_Print();
}
Py_DECREF(pResult);
}
//最后,不要忘记在完成后清理和关闭Python解释器:
Py_Finalize();
2.2 方法二,涉及大量二进制数据的传递:
Python的C API提供了将二进制数据传递给Python脚本的方式。你可以使用PyBytes_FromStringAndSize函数将C/C++中的二进制数据转换为Python的bytes对象,并将其传递给脚本。
事实上,这个参数最终仍然是通过Dictionary过去的。
//2进制模式传递参数的python-c互操作示例:
#include <Python.h>
int main(int argc, char *argv[]) {
Py_Initialize();
// 创建二进制数据
unsigned char binaryData[] = {0x01, 0x02, 0x03, 0x04, 0x05};
size_t binarySize = sizeof(binaryData);
// 将二进制数据转换为Python的bytes对象
PyObject *pBytes = PyBytes_FromStringAndSize((const char *)binaryData, binarySize);
// 将bytes对象传递给脚本
PyObject *pModule = PyImport_AddModule("__main__");
PyObject *pDict = PyModule_GetDict(pModule);
PyDict_SetItemString(pDict, "binary_data", pBytes);
// 执行脚本
const char *script = "print(binary_data)";
PyRun_String(script, Py_file_input, pDict, pDict);
// 释放Python对象
Py_XDECREF(pBytes);
Py_Finalize();
return 0;
}
3. 涉及data in => python => data out的情形,建议的调用模式
3.1 C一侧的代码示例
#define PY_RUN_STANDALONE
int all_python_funcXXXXX_of(cJSON *src, cJSON *FFT, const char *py_sh_filename)
{
if((src == NULL) ||(FFT==NULL)) return -1;
#ifdef PY_RUN_STANDALONE
Py_Initialize();
pModule = PyImport_AddModule("__main__");
pDict = PyModule_GetDict(pModule);
#endif
// 调用 Python 函数
const char *cstr = cJSON_Print(src);
PyObject *pObject = PyUnicode_FromString(cstr);
PyDict_SetItemString(pDict, "paramIn", pObject); //参数,传入paramIn字符串,直接在python可见
// 调用 Python 函数
FILE *file = fopen(py_sh_filename, "r");
if(file==NULL)
{
//deal error....
cJSON_AddItemToObject(FFT, "binData", cJSON_CreateString(""));
}
else
{
PyRun_File(file, py_sh_filename, Py_file_input, pDict, pDict);
PyObject *pKeys = PyDict_GetItemString(pDict, "resultOut"); //python的传出参数,也是字符串模式
if(pKeys == NULL)
{
cJSON_AddItemToObject(FFT, "binData", cJSON_CreateString(""));
}
else
{
char *keyStr = PyUnicode_AsUTF8(pKeys); //无需手工析构
cJSON *result = cJSON_Parse(keyStr);
cJSON *elem = result->child;
while (elem != NULL) {
printf("result of python:%s\n", elem->string); fflush(stdout);
cJSON_AddItemToObject(FFT, elem->string, cJSON_Duplicate(elem, 1));
elem = elem->next;
}
cJSON_Delete(result);
}
//PyDict_Clear(pDict);
close(file);
}
free(cstr);
#ifdef PY_RUN_STANDALONE
Py_Finalize();
#endif
return 0;
}
3.2 Python脚本一侧的框架示例
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# paramsIn => resultOut json => json.
import json
import binascii
import numpy as np;
import sys
#print("python version: %s", sys.version);
#c input params = >paramIn
jsonStr = paramIn;
#jsonStr = '{"binData": "48656c6c6f20576f726c6421"}'
#to json obj
data = json.loads(jsonStr)
###########################################################################
# Stage 1: here input json has been transfered to variable: data
# get hex buff. got an item form json.
hex_data = data['binData']
# get byte array.
byte_data = binascii.unhexlify(hex_data)
ba = bytearray(byte_data);
#计算FFT
signal = ba
fft_result = np.fft.fft(signal)
# 从 FFT 结果中提取幅度谱
amplitude_spectrum = np.abs(fft_result)
# 将幅度谱长度的一半舍去
half_length = len(amplitude_spectrum) // 2
amplitude_spectrum = amplitude_spectrum[:half_length]
# to hex again.
# 将浮点数组转换为 4 字节浮点型编码的二进制数据
ba = amplitude_spectrum.astype(np.float32).tobytes()
hex_data = binascii.hexlify(ba).decode('utf-8')
# create json obj again.
data = {'binData': hex_data}
root = {
'binData':hex_data,
'fft_result_pt':len(signal),
'fft_result_len': len(hex_data)
}
# Stage 2: here out json object has been established to object: root
###########################################################################
# json => final json_str_out
resultOut = json.dumps(root)
4. 循环调用、垃圾回收、异常捕获
如果仅仅是单次运行Python,上面的描述足够用。如果你需要周期性地调用Python代码。那么会有一些额外的问题需要考量,比如这个:
https://discuss.python.org/t/is-there-any-helpful-debug-tools-to-accelerate-c-python-debug/34449文章来源:https://www.toymoban.com/news/detail-692113.html
因为不同的.py共用一个执行环境,它会导致名字污染,在你无意识地重定义了一个built_in function 然后,很奇怪的事情就会出现。文章来源地址https://www.toymoban.com/news/detail-692113.html
4.1循环调用过程中的主动垃圾回收
//step 1.对最重要的两个外层交互用的Python对象添加引用计数
Py_Initialize();
pModule = PyImport_AddModule("__main__");
pDict = PyModule_GetDict(pModule);
Py_INCREF(pModule);
Py_INCREF(pDict);
//Step 2.退出时的反向操作
Py_DECREF(pModule);
Py_DECREF(pDict);
Py_DECREF(pDict); // 释放pDict对象的引用
//ResetPyEnvironment();
Py_DECREF(pModule); // 释放pModule对象的引用
Py_Finalize();
//Step3.主动在py脚本文件执行完毕后,调用py格式的垃圾回收脚本。
onst char *pyFileResetEnvironment = "./py/garbdge_collect.py";
void ResetPyEnvironment(void)
{
InvokePythonFile(pyFileResetEnvironment);
}
4.2相关的垃圾主动回收.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sys
import gc
#输出参数删除
resultOut = None
del resultOut
#输入参数删除
paramIn = None
del paramIn
gc.collect()
4.3 调试信息打印
void *ret = PyRun_File(file, py_sh_filename, Py_file_input, pDict, pDict);
if(ret ==NULL)
{
//when exception occur, can print line number of bug related line.
PyErr_Print();
printf(".py invoke error %s\n", py_sh_filename);fflush(stdout);
fclose(file);
exit(0);
}
附录A C与Python互操作需要的软件包
# python
yum install python3-devel
#互操作可能涉及到的cJSON
#需要下载cJSON-master源码:
cd /workerspace/sensor/src/data_dispatch/3rd/cJSON-master
make clean
make
make install
sudo ln -s /usr/local/lib/libcjson.so.1 /usr/lib/libcjson.so.1
sudo ldconfig
#gcc
yum install gcc-c++ cmake openssl-devel -y
#也许你会用到mqtt
#mosquitto这个也要下载源码包,包含服务器,但是可以不运行。
make clean
make
make install
sudo ln -s /usr/local/lib/libmosquitto.so.1 /usr/lib/libmosquitto.so.1
sudo ldconfig
#uuid.
yum install uuid-devel
yum install libuuid-devel
附录B 操控某个特定的python执行环境添加pip包的语法
#注意 -i --trusted-host的相对顺序,国外的服务器延迟太大,几乎无法访问
python3 -m pip install --timeout 200 --trusted-host mirrors.aliyun.com -i http://mirrors.aliyun.com/pypi/simple/ numpy
附录C 待执行的.python脚本的示例
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import json
import binascii
import numpy
import sys
print("python version: %s", sys.version);
#c input params = >paramIn
#jsonStr = paramIn;
jsonStr = '{"xxData": "48656c6c6f20576f726c6421"}'
#to json obj
data = json.loads(jsonStr)
# get hex buff.
hex_data = data['binData']
# get byte array.
byte_data = binascii.unhexlify(hex_data)
ba = bytearray(byte_data);
# print byte array.
print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>")
print(byte_data)
ba[0] = 0xff;
ba[1] = 0xff;
# to hex again.
hex_data = binascii.hexlify(ba).decode('utf-8')
# create json obj again.
data = {'binData': hex_data}
# json => str
json_string = json.dumps(data)
fft_result = json_string;
fft_result_len = len(json_string);
# print final output str.
print(json_string)
print("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<")
附录D 调用.py文件的 c函数示例
//python的传入参数部分已经介绍过,不再给出
//...
// 调用 Python 函数
FILE *file = fopen(pyFileFFT, "r");
if(file!=NULL)
{
PyRun_File(file, pyFileFFT, Py_file_input, pDict, pDict);
//传出参数解析
PyObject *ro = PyDict_GetItemString(pDict, "fft_result");
unsigned char *buf = PyByteArray_AsString(ro);
int len = PyLong_AsLong(PyDict_GetItemString(pDict, "fft_result_len"));
FFT = cJson_Parse(buf);
//...
到了这里,关于Linux c 与 python的互操作的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!