【问题标题】:returning tuple of list of lists of complex from c++ to python将复杂列表列表的元组从 c++ 返回到 python
【发布时间】:2019-01-29 20:44:19
【问题描述】:

我有一个PyObject* outvar 对象,它基本上构造为:

//some defs
typedef std::complex<double> Pt;
typedef std::vector<Pt> Pgon;
typedef std::vector<Pgon> Pgons;
PyObject* outvar = PyTuple_New(2);

auto outA = pgons2pylist(pA);
auto outB = pgons2pylist(pB);
PyTuple_SET_ITEM(outvar, 0, outp);
PyTuple_SET_ITEM(outvar, 1, outl);

在哪里pgons2pylist

PyObject* pgons2pylist(const Pgons& data) {
  PyObject* listObj = PyList_New( data.size() );
    for (unsigned int i = 0; i < data.size(); i++) {
        PyList_SET_ITEM(listObj, i, pgon2pylist(data[i]));
    }
    return listObj;
}

pgon2pylist 是:

PyObject* pgon2pylist(const Pgon& data) {
  PyObject* listObj = PyList_New( data.size() );
    for (unsigned int i = 0; i < data.size(); i++) {
        PyList_SET_ITEM(listObj, i, PyComplex_FromDoubles(data[i].real(),data[i].imag()));
    }
    return listObj;
}

我编译它并从py文件运行它:

mylib = ctypes.cdll.LoadLibrary('./mylib.so')
out_data = mylib.call(some_args)

out_data 始终是整数!如何转换为 [[complex]]?

【问题讨论】:

    标签: c++ python-3.x ctypes


    【解决方案1】:

    您构建 C 代码的方式看起来更接近于一个扩展模块 ([Python 3]: Extending Python with C or C++),而不是一个简单的.dll。检查 [SO]: Pass str as an int array to a Python C extended function (extended using SWIG) (@CristiFati's answer) 以获取方法之间的比较。

    然后,作为注释,您需要为导入的函数指定 argtypesrestype(这正是您获得 int)。检查[SO]: Python ctypes cdll.LoadLibrary, instantiate an object, execute its method, private variable address truncated,如果不这样做会发生什么。

    还列出了[Python 3]: ctypes - A foreign function library for Python 页面。

    关于代码的几点说明:

    所以,假设你有一个工作的 .dll,你会如何使用它(盲目地发布代码):

    mylib = ctypes.pydll.LoadLibrary('./mylib.so')
    outvar = ctypes.py_object.in_dll(mylib, "outvar")  # Note that you might have to declare it as extern "C", so its name doesn't get mangled
    

    @EDIT0

    我创建了一个虚拟示例来测试一切是否正常。

    dll.c

    #include <Python.h>
    
    #if defined(_WIN32)
    #  define EXPORT __declspec(dllexport)
    #else
    #  define EXPORT
    #endif
    
    
    EXPORT PyObject *tp = NULL;
    EXPORT int i = 123;
    EXPORT char *s = "Gainarie";
    EXPORT float f = -3.14;
    
    
    EXPORT void initTpl() {
        tp = PyTuple_New(2);
        PyTuple_SET_ITEM(tp, 0, PyLong_FromLong(7));
        PyTuple_SET_ITEM(tp, 1, PyLong_FromLong(-9));
    }
    

    code.py

    #!/usr/bin/env python3
    
    import sys
    import ctypes
    
    
    def main():
        dll = ctypes.PyDLL("./dll.so")
        i = ctypes.c_int.in_dll(dll, "i")
        s = ctypes.c_char_p.in_dll(dll, "s")
        f = ctypes.c_float.in_dll(dll, "f")
        dll.initTpl()
        tp = ctypes.py_object.in_dll(dll, "tp")
    
        print(i.value, s.value, f.value, tp.value, type(tp.value))
    
    
    if __name__ == "__main__":
        print("Python {:s} on {:s}\n".format(sys.version, sys.platform))
        main()
    

    注意事项

    • 我只在 Win 上进行了测试,因为我没有在我的 Lnx VM 上传输我的文件,但这不应该是问题
    • 由于它仅用于演示目的,我并不关心 内存泄漏(我也没有检查是否需要 Py_XDECREF

    输出

    e:\Work\Dev\StackOverflow\q054429301>dir /b
    code.py
    dll.c
    
    e:\Work\Dev\StackOverflow\q054429301>"c:\Install\x86\Microsoft\Visual Studio Community\2015\vc\vcvarsall.bat" x64
    
    e:\Work\Dev\StackOverflow\q054429301>cl /nologo /DDLL /MD /I"c:\Install\x64\Python\Python\03.06.08\include" dll.c  /link /NOLOGO /DLL /LIBPATH:"c:\Install\x64\Python\Python\03.06.08\libs" /OUT:dll.so
    dll.c
       Creating library dll.lib and object dll.exp
    
    e:\Work\Dev\StackOverflow\q054429301>dir /b
    code.py
    dll.c
    dll.exp
    dll.lib
    dll.obj
    dll.so
    
    e:\Work\Dev\StackOverflow\q054429301>"e:\Work\Dev\VEnvs\py_064_03.06.08_test0\Scripts\python.exe" code.py
    Python 3.6.8 (tags/v3.6.8:3c6b436a57, Dec 24 2018, 00:16:47) [MSC v.1916 64 bit (AMD64)] on win32
    
    123 b'Gainarie' -3.140000104904175 (7, -9) <class 'tuple'>
    

    【讨论】:

    • 问题是 (a) 没有复杂的 ctype (b) 没有向量 c_type (c) 我不能使用指针,因为每个列表的大小是未知的
    • 我不明白。这应该没有什么区别,因为您提到的所有内容都包含在 .dll 中,并且在 Python 方面,您根本不在乎,因为您得到的只是Python 兼容对象,基于您的代码 sn-ps 似乎是浮点列表的列表。
    • 那么如何在 Python 中定义 ctype 变量呢? python文件应该怎么看? 'myvar = ctype.???.in_dl(dll,'????')'
    • ctype 是什么意思?它应该只是一个 ctypes 类型,基本上是一个 C 包装器类型。没有C++。在我的示例中,我公开了此类类型。
    • 好吧,我返回一个 PyObject,它是复杂列表列表的元组,我怎样才能在 Python 中得到它(我需要做什么才能将指针转换为这个对象?)
    【解决方案2】:

    我从您的示例中了解到,您想从 Python 调用一个 C++ 函数,该函数接受一个复数列表并返回它们。

    如果您使用pybind11,您可以免费获得 std::vector 和 std::complex 的转换。 pybind11 是一个只有头文件的库。

    这是一个例子

    example.cpp

    #include <pybind11/pybind11.h>
    #include <pybind11/complex.h>
    #include <pybind11/stl.h>
    
    namespace py = pybind11;
    
    std::complex<double> foo(std::complex<double> a)
    {
        return a;
    }
    
    std::vector<std::complex<double>> foo2(std::vector<std::complex<double>>& v)
    {
        return v;
    }
    
    PYBIND11_MODULE(samplepy, m) {  
        m.def("foo", &foo, "Return the complex number entered as an argument");
        m.def("foo2", &foo2, "Return the list of complex number entered as an argument");
    }
    

    CMakeLists.txt

    cmake_minimum_required (VERSION 3.13)
    project (samplepy CXX)
    set(CMAKE_CXX_STANDARD 17)
    file (GLOB_RECURSE SOURCES "*.cpp" "*.h")
    
    if (WIN32)
        find_package(PythonInterp)
        find_package(PythonLibs 3.6 REQUIRED)
        set(PYBIND11_CPP_STANDARD /std:c++latest)
    else()
        find_package(PythonLibs 3.6 REQUIRED)
        set(PYBIND11_CPP_STANDARD -std=c++1z)
    endif()
    
    add_library (samplepy SHARED ${SOURCES})
    target_link_libraries(samplepy PRIVATE ${PYTHON_LIBRARIES})
    set_target_properties(samplepy PROPERTIES PREFIX "${PYTHON_MODULE_PREFIX}" SUFFIX "${PYTHON_MODULE_EXTENSION}")
    
    include_directories(${CMAKE_SOURCE_DIR}/include)
    include_directories(${PYTHON_INCLUDE_DIR})
    

    example.py

    import samplepy
    z = complex(2, -3)
    print(type(samplepy.foo(z)))
    print(samplepy.foo(z))
    
    l = [complex(1, -2), complex(3, -4)]
    print(type(samplepy.foo2(l)))
    print(samplepy.foo2(l))
    

    当我运行它时,我会在 py 控制台上得到它

    <class 'complex'>
    (2-3j)
    <class 'list'>
    [(1-2j), (3-4j)]
    

    【讨论】:

    • 编译后python报错说找不到samplepy,你把它放在.py文件旁边了吗?
    • 我在pythonpath环境变量stackoverflow.com/questions/19917492/how-to-use-pythonpath中添加了共享lib文件sample.py.so/dll所在的路径。我希望这有帮助。请将我的答案标记为正确
    • 您能详细说明一下吗?我试图在文件的开头添加 os.path.join(os.path.dirname(_file_)) 但它不起作用,也没有将它添加到 PYTHONPATH
    • 在运行 python.exe 之前的命令提示符中,您需要运行以下命令:bin 文件夹中的set PYTHONPATH=c:\dev\example\build\debug\bin;%PYTHONPATH% 应该包含您刚刚构建的 samplepy.pyd 文件以使其正常工作。
    猜你喜欢
    • 1970-01-01
    • 2017-11-28
    • 1970-01-01
    • 1970-01-01
    • 2014-07-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多