如何控制cython cdef类的模块/名称?

9 投票
2 回答
1437 浏览
提问于 2025-04-18 11:57

我正在使用cython把一个C++库暴露给Python,具体做法是把所有的包装对象和函数放在一个内部模块_pydynd中,然后通过另一个Python模块来暴露这些内容。
我想控制这些扩展类中显示的模块和类的名称,比如想让它们看起来像dynd.nd.array,而不是_pydynd.w_array,后者是包装类的内部名称。请问cython有没有办法做到这一点?

我希望能找到类似于在写C/C++函数定义时可以重命名的东西,但我搜索了很久都没有找到。应该要不同的生成C++代码是这里的tp_name这一行:

static PyTypeObject __pyx_type_7_pydynd_w_array = {
  PyVarObject_HEAD_INIT(0, 0)
  __Pyx_NAMESTR("_pydynd.w_array"), /*tp_name*/
  sizeof(struct __pyx_obj_7_pydynd_w_array), /*tp_basicsize*/

更新:

如果我直接尝试重命名这些对象,会发生这样的情况:

In [103]: nd.array.__name__
Out[103]: 'w_array'

In [104]: nd.array.__module__
Out[104]: 'dynd._pydynd'

In [105]: nd.array.__name__ = "array"
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-105-73d9172a0216> in <module>()
----> 1 nd.array.__name__ = "array"

TypeError: can't set attributes of built-in/extension type 'dynd._pydynd.w_array'

In [106]: nd.array.__module__ = "dynd.nd"
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-106-37f990658ede> in <module>()
----> 1 nd.array.__module__ = "dynd.nd"

TypeError: can't set attributes of built-in/extension type 'dynd._pydynd.w_array'

2 个回答

0

我有一个部分解决方案可以帮你解决问题,你可以把东西移动,或者说更好地导入到你的 __ init__.py 文件中的一个专用命名空间里。

让我们开始吧,你有一个库,里面有以下内容:

module.libname.A (A is a class for e.g. or anything else)
module.libname.B
...

现在我通过复制这些东西创建了自己的库。在你新模块里的 __ init__.py 文件中,你可以这样做:

__import__("module."+libname) # this import will import the library in the current context
globals().update(vars(module.libname))

# or use this
library = import_("PathToLibAndFilename", "module.")
globals().update(vars(library))

如果你遍历库中的内容(使用 vars(library)),你也可以重命名这些东西,不过我还没有尝试过。update() 需要一个字典,你可以传入一个你自己选择格式的字典。

你可以在控制台上试验一下(我觉得这对你来说是一个解决方案):

 import(libName)
 globals().update({"testMe":vars(libName)["A"]}) # A is your class name from the library
 # then you can do:
 testMe
 # in my case this outputs something like this: <class 'libName.A'>

附言:我使用 boost::python 来暴露我的类,但这只是对 cpython 内容的一个包装。

2

Cython-0.21 似乎是根据你的 .pyx 文件相对于 __init__.py 文件的位置来设置模块名称的。

对于文件布局

jmap/__init__.py
jmap/client.pyx

cython 编译器的输出包括

#define __Pyx_MODULE_NAME "jmap.client"

移动 __init__.py 文件的位置会改变这个值。这些信息可能在 Cython 的文档中,但我找不到。

讨论

正确设置这一点很重要,比如说要正确地序列化类。具体来说,一个模块对其名称的理解应该和你导入它的方式是对称的。这看起来很明显,但在 cython 中很容易出错。

好的

>>> from jmap.client import JMapError
>>> JMapError
<class 'jmap.client.JMapError'>

坏的

>>> import jmap
>>> jmap.JMapError
<class 'Client.client.JMapError'>

撰写回答