Cython: тип исключения для функции, возвращающей типизированное представление памяти - PullRequest
0 голосов
/ 04 июня 2018

В cdef подписи функции:

cdef const unsigned char[:, :] my_fn(input) except <????> :

Что я должен вставить в <????>?

Если я правильно понимаю документацию , указание типа исключения необходимо, чтобы исключение распространялось вверх по стеку Python.

Я пробовал вещи как [b'\x00'] и пустые массивы Cython, ни одна из них не работает.

1 Ответ

0 голосов
/ 04 июня 2018

Плохие новости: вы не можете этого сделать.Хорошие новости: вам не нужно это делать!

Синтаксис с except <xxx> возможен только в том случае, если функция cdef возвращает int, перечисление, число с плавающей запятой или указатель - в основном что-тодля которого имеет смысл сравнивать с помощью == в C.

Типизированное представление памяти - это объект Python, который имеет встроенный способ сообщить об ошибке - когда возвращаемый объект является нулевымуказатель.Таким образом, вам не нужно определять исключительное значение, потому что оно уже определено!

Например:

%%cython
cdef int[:] worker(int[:] memview, int index):
    memview[index]=10 
    return memview

def runit(index):
    cdef int mem[4]
    print(worker(mem,index))

А теперь

runit(4)   #4 -> out of bounds
print("I still run")

нетвыведите «Я все еще работаю», потому что распространено исключение «за пределами».

Это не относится к возвращаемому значению, которое не является объектом Python, например, int:

%%cython
cdef int worker(int[:] memview, int index):
    return memview[index]

А теперь:

runit(4)   #4 -> out of bounds
print("I still run")

печатает "0" и "Я все еще бегу", потому что ошибка не распространяется.Мы можем выбрать исключительное значение, например -1, чтобы ошибка распространялась через возвращаемое значение = -1:

%%cython
cdef int worker(int[:] memview, int index) except -1:
    return memview[index]

Теперь «Я все еще бегу» больше не печатается.

Однако иногда нет хорошего исключительного значения, например, потому что memview может содержать любое целочисленное значение:

%%cython
cdef int worker(int[:] memview, int index) except -1:
    return memview[index]

def runit(index):
    cdef int mem[4]
    mem[0]=-1
    print(worker(mem, index))

Теперь выполнение

runit(0)
print("I still run")

заканчивается ложной ошибкой:

SystemError: возвращено NULL без установки ошибки

Решение состоит в том, чтобы использовать

cdef int worker(int[:] memview, int index) except *

, который имеет правильное поведение для runit(0)и runit(4).

Так какова стоимость использования except * по сравнению с except -1?Они не очень высокие:

Если возвращаемое значение равно -1 (это «исключительное» значение по умолчанию), то мы знаем, что могла произойти ошибка (это только вероятность, а не уверенность)и проверьте через PyErr_Occurred(), действительно ли это так.

Как упомянуто в комментарии @DavidW, также можно использовать except? -1, что дает преимущество в том, что его легче читать и понимать.Самое смешное, что это даст тот же C-код, что и except *, потому что значение ошибки по умолчанию равно -1!

Однако синтаксис except? позволяет нам выбирать функциюРезультат, за который мы должны заплатить PyErr_Occurred().Например, если бы мы знали, что результат -1 встречается довольно часто, а -2 почти никогда, поэтому мы можем использовать except? -2, и PyErr_Occured() будет проверяться, только если результат функции равен -2,это означает почти никогда (в случае except * это будет проверяться довольно часто - каждый раз, когда возвращается -1).

...