Как мне управлять модулем / именем класса cython cdef?

#python #c #cython #cpython

#python #c #cython #cpython

Вопрос:

Я использую 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'
  

Ответ №1:

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'>
  

Комментарии:

1. Мне удалось заставить работать с pickle, назвав cython .pyx dynd. _pydynd.pyx вместо просто _pydynd.pyx. Это по-прежнему означает, что у него есть имя «dynd. _pydynd.w_array» вместо «dynd.nd.array», как я бы предпочел.

Ответ №2:

У меня есть частичное решение вашей проблемы, вы можете переместить или, лучше сказать, импортировать вещи в выделенное пространство имен в вашем __ init__.py файл.

Давайте начнем, у вас есть библиотека, в которой есть следующие вещи:

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

Теперь я создал свою собственную библиотеку на основе этого, скопировав настройки. В вашем __ init__.py найденный в вашем newmodule, вы можете сделать что-то вроде этого:

 __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 (библиотека), вы также можете переименовать сами элементы, но я пока этого не пробовал. update () нужен словарь, и вы можете передать словарь по вашему выбору в layout.

Вы можете поэкспериментировать с ним на консоли, используя (я думаю, это решение для вас):

  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'>
  

PS: Я использую boost:: python для предоставления своих классов, но это всего лишь оболочка для материалов cpython.

Комментарии:

1. Да, я уже делаю подобные вещи, но я хочу, чтобы имя и модуль отличались у объектов, чтобы они соответствовали общедоступному интерфейсу