Python C API使用时需要注意什么

供稿:hz-xin.com     日期:2025-01-15
python api

下个‘简明Python教程’跟一段时间就基本上知道了,加我吧,一起探讨~

ctypes: 可直接调用c语言动态链接库。
使用步骤:
1> 编译好自己的动态连接库2> 利用ctypes载入动态连接库3> 用ctype调用C函数接口时,需要将python变量类型做转换后才能作为函数参数,转换原则见下图:

4> Python若想获取ctypes调用的C函数返回值,需要先指定返回值类型。我们将在接下来的完整Sample中看到如何使用。
#Step 1: test.c#include int add(int a, int b){ return a + b;}#Step 2: 编译动态链接库 ( 如何编译动态链接库在本文不详解,网上资料一大堆。)gcc -fPIC -shared test.c -o libtest.so #Step 3: test.pyfrom ctypes import *mylib = CDLL("libtest.so") 或者 cdll.LoadLibrary("libtest.so") add = mylib.addadd.argtypes = [c_int, c_int] # 参数类型,两个int(c_int是ctypes类型,见上表)add.restype = c_int # 返回值类型,int (c_int 是ctypes类型,见上表)sum = add(3, 6)

一:用C API为Python写C语言函数,以方便Python中调用

1. 首先实现一个特定原型的函数,用Python C API来实现的话,所有函数必须是这种原型。必须是类似这样的
PyObject *Fun(PyObject *self, PyObject *args)
self应该是在用类的时候才会用到(我没有用到),args就是函数的参数。因为args是一个PyObject*类型(可以代表Python语言中的任何类型)
2. 将参数转换成C 语言表示的内容,用PyArg_ParseTuple函数。
3. 执行完需要的操作后,也必须返回一个PyObject*类型的值。通过Py_BuildValue函数来构建。
这里要说的是,假如希望返回一个Tuple类型的值,可以先用
PyObject *tuple = Py_BuildValue("(iis)", 1, 2, "three");
形式来构建,假如很多的话,可以用下面的方式来构建
PyObject *t;

t = PyTuple_New(3);
PyTuple_SetItem(t, 0, PyLong_FromLong(1L));
PyTuple_SetItem(t, 1, PyLong_FromLong(2L));
PyTuple_SetItem(t, 2, PyString_FromString("three"));
这一点在刚开始开工的时候迷惑了很久。
4. 将要输出的所有函数放入一个数组中,数组的结构是:
struct PyMethodDef {
const char *ml_name; /* The name of the built-in function/method */
PyCFunction ml_meth; /* The C function that implements it */
int ml_flags; /* Combination of METH_xxx flags, which mostly
describe the args expected by the C func */
const char *ml_doc; /* The __doc__ attribute, or NULL */
};
数组以{NULL, NULL}结束
5. 构造一个Python import时初始化的函数
类似
PyMODINIT_FUNC
initexample(void)
{
Py_InitModule("example", example_methods);
}
这里有个特别需要注意的是,初始化函数名字有严格要求,init后面必须跟模块名,否则Python找不到确定的函数会报没有初始化函数的错误

扩展模块写完后,编译成动态库(Python要求此动态库名字为pyd,实际就是改个后缀而已)。就可以直接在Python脚本中用import的方式加载了,对于使用来说,根本不需要知道此库是用C API扩展写的还是直接用Python语句写的(这点Lua做的也是一样好)
最后,python的源代码中附带了一个叫做example_nt的例子,可以参考一样,完整的扩展代码如下:
#include "Python.h"

static PyObject *
ex_foo(PyObject *self, PyObject *args)
{
printf("Hello, world/n");
Py_INCREF(Py_None);
return Py_None;
}

static PyMethodDef example_methods[] = {
{"foo", ex_foo, METH_VARARGS, "foo() doc string"},
{NULL, NULL}
};

PyMODINIT_FUNC
initexample(void)
{
Py_InitModule("example", example_methods);
}

二.C语言中调用Python语句
首先,void Py_Initialize()用来初始化,void Py_Finalize()用来结束Python的调用,这是必须要的。
燃火分两种情况,假如仅仅是几条语句的话,那么以PyRun_为前缀的一些函数都很好用,比如
int PyRun_SimpleString(const char *command)
函数就可以直接执行一条char*的Python语句。
需要获得返回值得话
PyObject* PyRun_String(const char *str, int start, PyObject *globals, PyObject *locals)
也很好用,以上两个函数用来处理Python源代码已经读入内存的情况,在文件中的时候
int PyRun_SimpleFile(FILE *fp, const char *filename)
PyObject* PyRun_File(FILE *fp, const char *filename, int start, PyObject *globals, PyObject *locals)
使用类似。不多讲了。
假如是个模块的话(比如一个函数),希望在C语言中调用的话那么使用起来就稍微复杂了一点。这种情况的需要在于你可以从C语言中向Python函数中传入参数并且执行,然后获取结果。
此处又分为几种情况:
在文件中,在内存中,编译过的,源代码。
在文件中都很好解决,和上面一样。这里主要讲在内存中的情况。(事实上我工作中需要并且耗费了很长时间才找到解决方法的就是这种情况)
未编译时:(也就是源代码)
1.通过
PyObject* Py_CompileString(const char *str, const char *filename, int start)
API首先编译一次。此API的参数我说明一下,str就是内存中的源代码,filename主要是出错时报错误用的,事实测试证明,你随意给个字符串也没有关系,但给NULL参数在运行时必然报错。start我一般用的是Py_file_input,因为的确是从文件中读取过来的,相对的还有Py_single_input用来表示一条语句,Py_eval_input的用法我也不是太清楚。
源代码通过此函数调用后,获得编译后的PyObject*,(其实假如跟进源代码中去看,是一个PyCodeObject结构)假设命名为lpCode。
2.此时再调用API
PyObject* PyImport_ExecCodeModule(char *name, PyObject *co)
导入模块。参数也说明一下,name为导入的模块名,co就是前面编译过的代码对象(lpCode)。返回的就是模块对象了,假设命名为lpMod。
3.再调用API
PyObject* PyObject_GetAttrString(PyObject *o, const char *attr_name)
获得函数对象。o就是模块对象(lpMod),attr_name就是你想要调用的函数名了,假设叫main的函数,就是”main”,然后返回的就是函数对象,假设命名为lpFun。
4.此时可以用API
int PyCallable_Check(PyObject *o)
去检查一下是不是获得了一个函数。假如确定的话,就可以直接用
PyObject_Call开头的一族函数调用lpFun了。这些函数包括很多,一般就是输入参数的不同,但是效果都是一样的,就是调用函数而已。参数一般可以通过前面说过的build函数来获得,返回值也是获得一个PyObject*,可以通过PyArg_那个函数来获取,但是好像不太好,那是分析参数用的。推荐用确定类型(假设为type)的类似Py[type]_As的函数来获取。
比如:
long PyLong_AsLong(PyObject *pylong)获取long
double PyLong_AsDouble(PyObject *pylong)获取double

这里想说的是,应该有直接从源代码中获取函数调用对象的方式,但是我本人没有试出来,有人知道请一定赐教!

编译过的代码:
对于编译过的代码和上面就是获得编译后的PyCodeObject对象,当然在源代码中表示还是PyObject*的方法不同(上例中的lpCode)。
当然要想以后获得一个编译后的lpCode,自然要先编译一下啦。但是纯粹编译成pyc结尾的文件后,直接读入内存,我没有找到将其转化为PyCodeObject对象的方法(也希望有人知道能告诉我!)

我找到的方法是先用
PyObject* PyMarshal_WriteObjectToString(PyObject *value, int version)
void PyMarshal_WriteLongToFile(long value, FILE *file, int version)
两个函数先把PyCodeObject对象(lpCode)序列化到文件或者内存中。
再在需要的时候用函数
PyObject* PyMarshal_ReadObjectFromFile(FILE *file)
PyObject* PyMarshal_ReadObjectFromString(char *string, Py_ssize_t len)
读出来,读出来的PyObject*其实就是想要的PyCodeObject对象了(lpCode)。接下来的步骤与未编译时的步骤一样。
光是这个扭曲的方法我还是参考老总给的半边资料反复研究出来的。而真正直接有效的方法我还是没有找到。

给Python 算法插上性能的翅膀——pybind11 落地实践
为了解决这一问题,本文将探讨如何利用pybind11在腾讯广告多媒体AI的Python算法中实现性能加速的落地实践,以及在这一过程中总结的经验。首先,让我们回顾一下目前业内针对Python与C\/C++交互的主流方案。业内方案概览 原生方案与Cython - **Python\/C API**:直接通过C代码与Python交互,但需要手动处理所有...

python ctypes使用
除了ctypes之外,还有其他选项如Cython、Python C API、Swig和pybind11,可用来调用C\/C++函数或整合第三方工具包。ctypes数据类型和调用函数时,需要注意参数类型设置,ctypes.RTLD_GLOBAL、ctypes.RTLD_LOCAL和ctypes.DEFAULT_MODE等加载选项定义了动态库的符号可见性范围。在使用ctypes加载动态链接库时,要确保...

如何在Python中调用C\/C++函数
Python调用C库的常见方式之一是使用Python内置的标准库ctypes,它主要用于使用C和C++库,也可以将ctypes与用任何语言编写的库一起使用,只要这些库导出与C兼容的API即可。使用ctypes的优势在于,它已经包含在Python中,理论上可以调用任何C或C++共享库或动态库。使用ctypes的另一个优点是不需要重新编译库就...

python怎么实现调用c\/cpp的库?
通过研究发现,将复杂类型直接定义为ctypes.c_void_p类型,并使用buffer_type(tensor.data_ptr())即可将PyTorch的tensor对象传递到动态链接库中。总结而言,本文提供了一种在特定情况下使用纯Python代码直接调用动态链接库的方法。这种方法适用于API接口基本不变,但需要频繁在不同版本的动态链接库之间切换的...

nuitka编译器(gcc by clang)安装指南
Nuitka 是一个将 Python 代码编译为等效的 Python-C-API 然后进行打包的工具。为了使用它,你需要在编译 C 代码时选择合适的 C 编译器。在Windows 环境下,常见的 C 编译器包括 MSVC、Gcc(也称为 mingw64)、clang。对于 MSVC 的安装,你可以在安装 Visual Studio 的过程中找到相应的教程。本篇...

CPython源码学习:5、Python如何加载so\/pyd动态库?
至此,理解了Python如何读取动态库文件,可以着手编写CPython扩展库。编写扩展库涉及调用CPython的C-API。首先需要定义一个入口函数PyInit_mymath,并返回一个PyModuleDef类型。接着,在库文件中定义m_methodes,这里定义了一个名为devision的方法,对应C代码中的division函数,该函数接收两个long变量并求出...

在python中如何调用PCSC中的接口
你找个工具将这个接口API,自动转换成python版本的就可以了。好象是SWIG和Boost。 这两个以前看过,偶尔还试过几次。之所以建议你用自动工具,就是因为python对于C++扩展麻烦些,对于C语言接口可以使用ctypes和cython简单解决。不过C++如果用这些方法有时候被很麻烦。boost在linux里会经常用到,在windows下也...

cython与python的不同有哪些
原生Python数据结构有一点加速 Python提供了大量的数据结构 - 字符串,列表,元组,字典等等。它们对于开发者来说非常方便,而且他们自带了自动内存管理功能,但是他们比纯C慢。Cython让你继续使用所有的Python数据结构,尽管没有太多的加速。这又是因为Cython只是在Python运行时调用创建和操作这些对象的C API...

python调用c++接口?
使用g++编译生成C动态库的代码中的函数或者方法时,需要使用extern"C"来进行编译。\\x0d\\x0a(3)Python调用动态库的文件:pycall.py\\x0d\\x0a\\x0d\\x0a[html]viewplaincopy\\x0d\\x0aimportctypes\\x0d\\x0all=ctypes.cdll.LoadLibrary\\x0d\\x0alib=ll(".\/libpycall.so")\\x0d\\x0alib.foo(1,3)\\...

用Python做一个"以图搜番"的应用程序,再也不用愁动漫图片的出处了!_百 ...
普通用户每天的查询次数限制为150次。在使用API时需注意遵守服务条款。最后,使用Nuitka将Python代码打包成.exe文件,以提高执行速度和优化用户体验。Nuitka是将Python代码转换为C\/C++代码的工具,从而实现更快的执行速度。打包步骤包括下载并配置MinGW64,安装Nuitka,调试阶段逐个加入所需的轮子文件,以及生成...