Указатель на функцию в типе расширения Cython - PullRequest
1 голос
/ 30 апреля 2019

Я пишу модуль Cython, который предоставляет несколько типов расширений, которые используют оптимизированные функции cdef.Некоторые из этих типов расширений (около 10, каждый из которых содержит около 200 строк кода) имеют абсолютно одинаковую структуру, но не вызывают одинаковые cdef функции.Я хотел бы разложить мой модуль так, чтобы только один тип расширения мог обрабатывать все необходимые мне конфигурации.

Чтобы прояснить ситуацию, вот (очень глупый) пример структуры модуля, который я пишу:

cdef class A1:

    cdef double x

    def __init__(self, double x):
        self.x = x

    def apply(self):
        self.x = f1(self.x)

cdef class A2:

    cdef double x

    def __init__(self, double x):            
        self.x = x

    def apply(self):
        self.x = f2(self.x)       

cdef double f1(double x):
    return x**1

cdef double f2(double x):
    return x**2

...

и вид факторизованного кода, который я хотел бы получить:

cdef class A:

    cdef int n
    cdef double x

    def __init__(self, double x, int n):
        self.x = x
        self.f = globals()[f'f{n}']

    def apply(self):
        self.x = self.f(self.x)

В чистом Python такую ​​структуру легко настроить с помощью globals или getattr, но в Cython cdef объекты (конечно) недоступны из Python, поэтомув globals().

Я полагаю, что реализация такого рода кода C будет использовать указатели на функции, но я не могу найти, как это сделать внутри типа расширения Cython.На самом деле реальный вопрос заключается в том, можно ли добавить указатель на функцию cdef в качестве атрибута экземпляра (самостоятельно)?Если нет, то как я могу разложить этот тип кода без потери производительности (т.е. без изменения моих функций cdef)?

1 Ответ

2 голосов
/ 03 мая 2019

Вы можете использовать указатель на функцию, но с немного большим количеством стандартного кода, чем в чистом Python, например:

%%cython

# here all possible functions double->double
cdef double fun1(double x):
    return 2*x

cdef double fun2(double x):
    return x*x;
...

# class A wraps functions of type double->double
ctypedef double(*f_type)(double) 

# boiler-plate code for obtaining the right functor
# uses NULL to signalize an error
cdef f_type functor_from_name(fun_name) except NULL:
    if fun_name == "double":
        return fun1
    elif fun_name == "square":
        return fun2
    else:
        raise Exception("unknown function name '{0}'".format(fun_name)) 


cdef class A:
    cdef f_type fun

    def __init__(self, fun_name ):
        self.fun = functor_from_name(fun_name)

    def apply(self, double x):
        return self.fun(x)

Мне неизвестна возможность получить указатель на cdef -функцию от имени функции во время выполнения, и я не думаю, что есть один из готовых.

А теперь работает как рекламируется:

>>> doubler = A("double")
>>> double.apply(3.0)
6.0
>>> squarer = A("square")
>>> squarer.apply(3.0)
9.0
>>> dummy = A("unknown")
Exception: unknown function name 'unknown'
...