Python中如何生成DLL或SO文件以便调用
目前的环境
vs2017 python3.6.8
因为要用 seal 库,所以选择生成 dll 来方便 python 调用
样例 util.cpp
#include <iostream>
...
#include "seal/seal.h"
shared_ptr<SEALContext> createContext() { … }
vector<string> { … }
string hamming() { … }
生成了 dll 和 lib 文件 但到 python 上调用就是 AttributeError: function not found
网上找了很多 csdn 的文章,没啥帮助,我还想移植到 linux 上
seal 库会有个 seal.lib 要用到
请求各位能帮个忙~
感激不尽
Python中如何生成DLL或SO文件以便调用
大概是因为没加 extern “C”,实际函数名称是 C++ 修饰过的。
另外更建议你用 pybind11 写成 Python 模块来使用。
在Python中,通常不会直接生成DLL(Windows动态链接库)或SO(Linux共享对象)文件,因为Python本身是解释型语言。但你可以通过以下几种方式实现类似功能,让其他语言(如C/C++)调用Python逻辑,或者反过来,让Python调用C/C++编译的库。
1. 使用Cython将Python代码编译为C扩展模块
Cython可以将Python代码(尤其是带有类型声明的)编译成C代码,然后进一步编译成平台相关的动态库(.pyd文件在Windows上,本质也是DLL;.so文件在Linux上)。这样,其他Python程序可以直接import使用,但如果你想从纯C/C++程序调用,需要编写额外的C封装层。
示例步骤:
- 安装Cython:
pip install cython - 编写一个简单的Python模块,比如
mymodule.pyx:
# mymodule.pyx
def add(int a, int b):
return a + b
- 创建
setup.py:
from setuptools import setup
from Cython.Build import cythonize
setup(
ext_modules = cythonize("mymodule.pyx")
)
- 编译:在终端运行
python setup.py build_ext --inplace。这会生成mymodule.c和对应的动态库文件(如mymodule.cp39-win_amd64.pyd或mymodule.so)。 - 在Python中直接导入使用:
import mymodule; print(mymodule.add(2, 3))
2. 使用ctypes或cffi从Python调用已有的C/C++ DLL/SO
如果你的目标是用Python调用已有的C/C++库,这是最直接的方式。你只需要有编译好的DLL或SO文件,以及对应的函数签名。
ctypes示例(调用一个假设的add函数):
import ctypes
# 加载库
lib = ctypes.CDLL('./mylib.so') # Linux
# lib = ctypes.CDLL('mylib.dll') # Windows
# 指定参数和返回类型
lib.add.argtypes = [ctypes.c_int, ctypes.c_int]
lib.add.restype = ctypes.c_int
# 调用
result = lib.add(5, 3)
print(result) # 输出 8
3. 使用C/C++编写扩展模块并编译为Python可导入的库
这是最传统的方式,直接用C/C++编写Python扩展模块,通过Python的C API与Python交互。这需要你熟悉Python C API。
简单示例(C代码):
// mymodule.c
#include <Python.h>
static PyObject* add(PyObject* self, PyObject* args) {
int a, b;
if (!PyArg_ParseTuple(args, "ii", &a, &b))
return NULL;
return PyLong_FromLong(a + b);
}
static PyMethodDef MyMethods[] = {
{"add", add, METH_VARARGS, "Add two integers."},
{NULL, NULL, 0, NULL}
};
static struct PyModuleDef mymodule = {
PyModuleDef_HEAD_INIT,
"mymodule",
NULL,
-1,
MyMethods
};
PyMODINIT_FUNC PyInit_mymodule(void) {
return PyModule_Create(&mymodule);
}
编译同样使用setup.py(但用Extension):
from setuptools import setup, Extension
module = Extension('mymodule', sources=['mymodule.c'])
setup(
name='MyModule',
ext_modules=[module]
)
运行python setup.py build_ext --inplace生成动态库。
总结建议
根据你的具体需求(是让Python调C,还是让C调Python,或者只是加速Python代码)选择合适的方法。
如果你想用 ctypes 或者 cffi 来调用,请用纯 C 函数,否则会遇到无尽的坑(虽然加 extern “C” 能解决一部分问题,但是如果你返回 STL 类型,在 python 层还是无法操作)
哈哈,昨天刚编译完扩展库,顺嘴答一下
题主可以考虑使用 boost::python 将 C++代码导出到 python,windows 的话用 VS 直接编译成 pyd 调用就好; Linux 的话用 python 自带的 distutils 调用 gcc 编译成 so 使用
记得注意代码的跨平台兼容性哦,如果决定使用 boost::python 导出,可以尽量把 std 里一些东西换成 boost 里的,对减少编译错误有帮助。
最后祝题主成功!
#1 extern "C"用了,显然无用,估计是因为我的返回类型有 vector<string>这种 看样子只能用 pybind11 写了,顺带问下 pybind11 可以这样写吗?感谢~
#2 感谢~了解了。不知楼上说的 pybind11 能否解决?
#3 ok ok,我去试试,有问题再问你,感谢回复~
有传递 STL 类型的话显然是不能直接 extern “C” 的。
pybind11 应该是支持自动转换的
https://pybind11.readthedocs.io/en/stable/advanced/cast/stl.html
https://github.com/pybind/pybind11/blob/master/tests/test_stl.py
https://github.com/pybind/pybind11/blob/master/tests/test_stl.cpp
#6 ok,我还用到了扩展库 seal,让我试下,谢谢您~
同时不太推荐 boost.python,因为:
boost 需要手动编译,而 pybind11 是 headers only 的,直接 include 就能用。
pybind11 有配套的 cmake 脚本,可以方便地实现跨平台编译。
这种还真不知道。
我这两天用了 cython 把 python 源码转成 so,手动步骤就是 py->c->so
另外,Linux 一般做法是 写一个 gir 之类的绑定,不知道 Windows 是怎么做的
你可以学习一下 Python C API,用 C 实现个函数返回一个包含一堆 PyUnicodeObject 的 PyListObject。
pybind11 我没有用过,不是很了解,我自己的做法是自己用 C 写个兼容层,然后在 python 那边用代码包装一下,当然这样的性能并不高,看 pybind11 的文档应该更适合解决你的问题

