【问题标题】:How to use an object of a wrapped class as an argument in a wrapped function如何使用包装类的对象作为包装函数中的参数
【发布时间】:2020-01-23 12:33:31
【问题描述】:

我使用 cython 来包装 C++ 代码并使其在 Python 中可用。我面临的问题是我想使用一个包装的类作为我想要包装的函数中的参数。因此,从 Python 的角度来看,我想创建和修改包装类的对象,并将其用作我也想从 Python 调用的包装函数的参数。下面的代码有望证明这一点。

您可以在下面找到我想要包装的 C++ 中的最小示例:

./cppCode/Settings/Settings.h

class Settings
{
public:
  Settings();
  void doSomething();
};

./cppCode/Helper/Helper.h

#include "../Settings/Settings.h"

void myFunction(Settings settings);

功能并不重要。因此,我省略了 .cpp 文件。以下是到目前为止我在 Cython 中的方法:

./cythonCode/Settings/Settings.pxd

cdef extern from "../../cppCode/Settings/Settings.h":
  cdef cppclass Settings:
    Settings() except +
    void doSomething()

./cythonCode/Settings/Settings.pyx

# distutils: sources = ../../cppCode/Settings/Settings.cpp
# distutils: language = c++

from Settings cimport Settings

cdef class PySettings:
  cdef Settings c_settings

  def __cinit__(self):
    self.c_settings = Settings()

  def doSomething(self):
    self.c_settings.doSomething()

./cythonCode/Helper.pxd

from Settings.Settings cimport Settings

cdef extern from "../../cppCode/Helper/Helper.h":
  void myFunction(Settings settings)

./cythonCode/Helper.pyx

# distutils: sources = ../../cppCode/Helper/Helper.cpp
# distutils: language = c++

from Helper cimport myFunction

cdef PyMyFunction(PySettings settings):
  myFunction(settings)

运行.py

import cythonCode.Settings.Settings as Settings
#import cythonCode.Helper as Helper

mySettings = Settings.PySettings()
mySettings.doSomething()

#Helper.myFunction(mySettings) # not working

我希望项目的结构是清晰的。我实际上也想在文件夹“Helper”中拥有“Helper.pyx”和“Helper.pxd”,但是我不知道如何导入设置。如果您能帮我解决这个问题,我们将不胜感激。但是,主要问题是让 Helper 完全运行,以便我可以在“run.py”中使用它。 构建 cython 模块的“setup.py”看起来像这样:

from distutils.core import setup
from Cython.Build import cythonize
from setuptools.extension import Extension

extensions = [
  Extension("Helper", ["Helper.pyx"])
]

setup(ext_modules=cythonize(extensions))

我在设置文件夹中单独做同样的事情。

非常感谢您帮助解决此问题!

EDIT:如cmets中所说,有两个错误:

1) 在run.py中应该是Helper.PyMyFunction(mySettings)。

2) PyMyFunction前面应该是def而不是cdef,因为我肯定想从Python调用这个函数。

EDIT2:我玩弄了您的输入,发现了一个不雅的解决方案,这导致了另一个问题。所以下面是对我有用的代码:

设置.pxd

cdef extern from "../../cppCode/Settings/Settings.h":
  cdef cppclass Settings:
    Settings() except +
    void doSomething()

cdef extern from "../../cppCode/Helper/Helper.h":
  void myFunction(Settings settings)

Settings.pyx

# distutils: sources = [../../cppCode/Settings/Settings.cpp, ../../cppCode/Helper/Helper.cpp]
# distutils: language = c++

from Settings cimport Settings, myFunction

cdef class PySettings:
  cdef Settings c_settings

  def __cinit__(self):
    self.c_settings = Settings()

  def doSomething(self):
    self.c_settings.doSomething()

def PyMyFunction(PySettings settings):
  myFunction(settings.c_settings)

当我对 Settings.pyx 进行 cythonize 时,我可以运行以下 Python 代码并且一切正常:

import Settings

mySettings = Settings.PySettings()
mySettings.doSomething()

Settings.PyMyFunction(mySettings)

我觉得不雅之处在于这两个部分(设置和 myFunction)都包含在同一个文件中。当这两个部分位于不同的文件中时,我不知道如何运行它。

EDIT3:为了解决“两个部分在不同文件中”的问题,请想象以下代码:

设置.pxd

cdef extern from "../../cppCode/Settings/Settings.h":
  cdef cppclass Settings:
    Settings() except +
    void doSomething()

Settings.pyx

# distutils: sources = ../../cppCode/Settings/Settings.cpp
# distutils: language = c++

from Settings cimport Settings

cdef class PySettings:
  cdef Settings c_settings

  def __cinit__(self):
    self.c_settings = Settings()

  def doSomething(self):
    self.c_settings.doSomething()

Helper.pxd

from Settings cimport Settings

cdef extern from "../../cppCode/Helper/Helper.h":
  void myFunction(Settings settings)

Helper.pyx

# distutils: sources = ../../cppCode/Helper/Helper.cpp
# distutils: language = c++

from Helper cimport myFunction

def PyMyFunction(PySettings settings):
  myFunction(settings.c_settings)

它与 EDIT2 中的代码相同,但分为两个文件。 Helper.pxd 中只有一行是“来自设置 cimport 设置”。但是,在 Helper.pyx 中出现此错误:

def PyMyFunction(PySettings settings):
                ^
---------------------------------------
Helper.pyx:6:17: 'PySettings' is not a type identifier

我尝试了“从设置 cimport PySettings”,但这不起作用。同样的错误不断发生。所有文件都在同一个目录中。

【问题讨论】:

  • @ead 我不相信这是正确的副本(或者如果它是正确的,我不太清楚)。这里的问题是1)PyMyFunctioncdef而不是def,所以不能从Python调用; 2)OP调用myFunction(没有Py),我怀疑这只是一个错字。我很确定第 1 点有重复...
  • 鉴于您的编辑:您有什么问题?我可以在您的代码中看到一些小东西(您可能没有构建设置模块;不需要 cimport 一个同名的 pxd;在 helper.pxd 中您可能也想导入 PySettings),但这看起来基本正确,我认为错误消息应该足够清晰,您可以修复它们。
  • 我进行了第二次编辑。现在基本上是一个完全不同的问题。除非这很容易解决,否则我可能应该打开一个新问题。

标签: python c++ cython cythonize


【解决方案1】:

很明显,这个问题只是一系列小问题,但已经接近正常工作:

  1. 最初的主要问题是函数 [应该是 defcpdef,但不是 cdef 可以从 Python 调用 (Importing cython function: AttributeError: 'module' object has no attribute 'fun')。值得记住的是,Cython 编译/加速所有函数,cdefcpdef 的唯一优点是它们可以从 Cython 调用稍快一些。因此,您应该默认声明函数cdef

  2. 在某些情况下,您使用 C++ 类型名称 Settings 而不是 Cython 类 PySettings。我认为这主要是一个错字。

  3. 无需在 Settings.pyx 中执行 cimport Settings 并在 Helper.pyx 中执行 cimport Helper - Cython 会自动执行与 from filename cimport * 等效的操作,其中存在具有匹配文件名的 .pxd 文件。

  4. 您的最后一个问题是将您的类分成多个文件 - 您无法将 cimport PySettings 放入 Helper.pyx。您应该记住的是“cimport”查看 .pxd 文件 - 将其视为 C/C++ 头文件。如果你想cimport PySettings那么(声明)PySettings必须在Settings.pxd中

    cdef class PySettings:
       cdef Settings c_settings
       # signatures for any cdef functions also go here
    

    在 Settings.pyx 中你这样做

    cdef class PySettings:
        # don't duplicate "cdef Settings c_settings"
        # do put all the def functions here
        def __cinit__(self):
           self.c_settings = Settings()
    
        def doSomething(self):
           self.c_settings.doSomething()
    

    从这个 Helper.pyx 将知道 PySettings 以及它具有 c_settings 属性的事实。

【讨论】:

  • 非常感谢您提供的所有有用信息!基本解决了这个问题。但是,python 代码中的导入仍然存在问题。我为此提出了一个不同的问题:stackoverflow.com/questions/58083432/…
猜你喜欢
  • 2018-04-27
  • 2013-03-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-15
  • 2020-08-31
  • 2021-11-17
相关资源
最近更新 更多