Как поймать исключение в декораторе
У меня есть функция, моя причина исключения, и я хочу, чтобы она была декоратором. Код выглядит следующим образом:
def des(i):
def new_func(func):
if i == 1:
raise Exception
else:
return func
return new_func
@des(1)
def func():
print "!!"
if __name__ == '__main__':
try:
func()
except Exception:
print 'error'
но вывод:
Traceback (most recent call last):
File "D:/des.py", line 10, in <module>
@des(1)
File "D:/des.py", line 4, in new_func
raise Exception
Exception
Итак, как я могу поймать это исключение?
4 ответа
Как объясняли другие ответы, ваша текущая проблема заключается в том, что вы получаете исключение, когда декоратор применяется к функции, а не когда вызывается функция.
Чтобы это исправить, вам нужно заставить декоратор возвращать функцию, которая вызывает исключение. Вот как это может работать:
import functools
def des(i):
def decorator(func):
if i != 1:
return func # no wrapper needed
@functools.wraps(func)
def raiser(*args, **kwargs):
raise Exception
return raiser
return decorator
des
функция "декораторская фабрика". Это на самом деле не делает ничего, кроме предоставления возможности для удержания i
параметр для декоратора, который он возвращает.
decorator
Функция проверяет, нужно ли делать что-то особенное. Если нет, то возвращает декорированную функцию без изменений. Если i==1
, он возвращает пользовательскую функцию.
raiser
Функция является возвращаемым значением декоратора, если i==1
, Это всегда вызывает исключение, когда оно вызывается. functools.wraps
декоратор, применяемый к нему, не является строго необходимым, но он делает его более похожим на оригинальную функцию (то же самое __name__
, __doc__
, так далее).
Исключение возникает при определении функции. Единственный способ поймать это исключение:
try:
@des(1)
def func():
print '!!'
except:
print 'error'
Если сбивает с толку, почему это вызывает исключение, помните, что ваш код эквивалентен:
def func():
print '!!'
func = des(1)(func)
# des(1) = new_func, so des(1)(func) is new_func(func)
Итак, сейчас ваш код сводится к следующему:
_des = des(1)
def _func();
print '!!'
func = _des(func)
Вы используете возвращаемое значение des
как декоратор, и я думаю, что это является причиной проблемы.
Я думаю, что вы можете захотеть вложить эту возвращенную функцию еще раз:
def des(i): # container func.
def new_func(func):
def ret_func(*args, **kwargs):
if i == 1:
raise Exception
else:
return func(*args, **kwargs)
return ret_func # return the func with the bound variable
return new_func # return the func which creates the function w/ the bound var.
@des(1)
def func():
print "!!"
Мне не хватает одного функционального уровня здесь.
ITYM
import functools
def des(i): # this is the "decorator creator", called with des(1)
def deco(func): # this is returned and is the real decorator, called at function definition time
@functools.wraps(func) # sugar
def new_func(*a, **k): # and this is the function called on execution.
if i == 1:
raise Exception # I hope this is just for testing... better create a new exception for this
else:
return func(*a, **k)
return new_func
return deco
@des(1)
def func():
print "!!"
if __name__ == '__main__':
try:
func()
except Exception:
print 'error'