如何在Python中使用C++类?

49

我已经在 C++ 中实现了一个类,现在我想在 Python 中使用它。 请提供逐步详细的方法,并对每个步骤进行详细解释。 类似于这样...

class Test{
     private:
        int n;
     public:
        Test(int k){
            n=k;
        }
        void setInt(int k){
            n = k; 
        }
        int getInt(){
            return n;
        }
};

现在,在Python中

>>> T1 = Test(12)
>>> T1.getInt()
12
>>> T1.setInt(32)
>>> T1.getInt()
32

请建议。我该如何做到这一点? 注意:我想知道手动完成的方式。我不想依赖任何第三方库。


1
只是好奇 - 你为什么想避免第三方? - Andrew Hare
4个回答

28

了解一下Boost.Python。它是一个用C++编写Python模块的库。

还要看看SWIG,它也可以处理其他脚本语言的模块。我过去使用它为我的课程编写模块并在Python中使用。效果很好。

你也可以使用Python/C API手动完成,自己编写接口。这是相当底层的操作,但你将会获得更多关于Python背后运行方式的知识(当然,如果你使用SWIG也需要这些知识)。


22
不符合“我不想依赖任何第三方库”的要求。 - Constantin
3
无论如何,您都需要Python库。当然,它包括Python/C API。 - Johannes Schaub - litb

13

ctypes很好用,而且它是Python自带的,使用起来非常容易。不幸的是,它只能与具有C风格接口的共享库(Unix)或DLL(Windows)通信,这意味着您无法直接与C++对象进行接口。但是,您可以使用句柄系统,其中句柄指向特定的对象。

>>> getInt(h)
12

我认为这很简单,易于理解,并且不需要额外的库。


2
请您详细解释一下“您无法与共享库或DLL进行通信,这意味着您无法直接与C++对象进行接口交互”的含义。在Python中使用C++对象会有什么行为上的变化?这会产生明显的影响吗?我最终需要在Python中编写一个绑定到我的C++ API,非常感谢您提供的任何帮助。 - Kyle Bridenstine
4
@2g7hgfdr8324,你误引了我的话。我没有说“你不能与共享库或DLL交互”。我说的是相反的。如果你能将你的C++ API公开为DLL,在接口上你需要使用纯C,而不是C++,那么你就可以通过ctypes从Python中调用它。 - dan-gph

8
这是一篇非常老的帖子,可能原作者已经找到了解决方案,但由于还没有人提供有效的示例,我会尝试并希望能帮助像我一样遇到同样问题的人。
由于原作者没有具体说明,我将限制我的回答在Windows上,虽然Linux的解决方案很简单。我将尝试提供一个最小化的工作示例来复现所讨论的代码。
步骤1)从C++代码(单个文件)开始。
class Test{
     private:
        int n;
     public:
        Test(int k){
            n=k;
        }
        void setInt(int k){
            n = k;
        }
        int getInt(){
            return n;
        }
};

extern "C" 
{
    // include below each method you want to make visible outside
    __declspec(dllexport) Test* init(int k) {return new Test(k);}
    __declspec(dllexport) void setInt(Test *self, int k) {self->setInt(k);}
    __declspec(dllexport) int getInt(Test *self) {return self->getInt();}
    
    // Note: the '__declspec(dllexport)' is only necessary in Windows
}

第二步) 编译DLL共享库。例如,从您的工作目录:
g++ -shared mytest.cpp -o libtest.dll
第三步) 创建包装器Python文件:
#  mytest.py

import ctypes
import platform

# From Python 3.8 onwards, there is a reported bug in CDLL.__init__()
mode = dict(winmode=0) if platform.python_version() >= '3.8' else dict()  


lib = ctypes.CDLL('./libtest.dll', **mode)


class Test(object):
    def __init__(self, val: int):
        # Declare input and output types for each method you intend to use
        lib.init.argtypes = [ctypes.c_int]
        lib.init.restype = ctypes.c_void_p

        lib.setInt.argtypes = [ctypes.c_void_p, ctypes.c_int]
        lib.setInt.restype = ctypes.c_void_p

        lib.getInt.argtypes = [ctypes.c_void_p]
        lib.getInt.restype = ctypes.c_int

        self.obj = lib.init(val)

    def setInt(self, n):
        lib.setInt(self.obj, n)
    
    def getInt(self):
        return lib.getInt(self.obj)



T1 = Test(12)
print(T1.getInt())
T1.setInt(32)
print(T1.getInt())

步骤4)运行代码

当我使用4个不同的Python版本运行脚本时,以下是我的结果:

PS D:\Desktop\mytest> py -3.7 .\mytest.py
12
32
PS D:\Desktop\mytest> py -3.8 .\mytest.py
12
32
PS D:\Desktop\mytest> py -3.9 .\mytest.py
12
32
PS D:\Desktop\mytest> py -3.10 .\mytest.py
Traceback (most recent call last):
  File "D:\Desktop\mytest\mytest.py", line 7, in <module>
    lib = ctypes.CDLL('./libtest.dll', **mode)
  File "D:\Python\3102\lib\ctypes\__init__.py", line 374, in __init__
    self._handle = _dlopen(self._name, mode)
FileNotFoundError: Could not find module 'D:\Desktop\mytest\libtest.dll' (or one of its dependencies). Try using the full path with constructor syntax.

在最后一种情况下,我已经尝试了完整路径,但仍然出现相同的错误。
我正在运行Windows 10 64位,并使用MinGW64中的g ++ 8.1.0进行编译。
评论:
我的答案基于此处此处此处提供的解决方案。
当Python和编译器(g ++)架构不匹配(即32位或64位)时,常见问题会出现。

在您找到部分解决方案的同一线程中,有一个答案说从Python 3.9开始,它只能使用winmode=1。在您的情况下,它从v3.10失败,但可能与此相关,我自己还没有复制它。 - Amo Robb

6

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接