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文件以便调用

12 回复

大概是因为没加 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封装层。

示例步骤:

  1. 安装Cython:pip install cython
  2. 编写一个简单的Python模块,比如mymodule.pyx
# mymodule.pyx
def add(int a, int b):
    return a + b
  1. 创建setup.py
from setuptools import setup
from Cython.Build import cythonize

setup(
    ext_modules = cythonize("mymodule.pyx")
)
  1. 编译:在终端运行 python setup.py build_ext --inplace。这会生成mymodule.c和对应的动态库文件(如mymodule.cp39-win_amd64.pydmymodule.so)。
  2. 在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,我去试试,有问题再问你,感谢回复~

#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 的文档应该更适合解决你的问题

回到顶部