Плохие новости: вы не можете этого сделать.Хорошие новости: вам не нужно это делать!
Синтаксис с 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
).