Как поймать исключение в декораторе

У меня есть функция, моя причина исключения, и я хочу, чтобы она была декоратором. Код выглядит следующим образом:

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'
Другие вопросы по тегам