Декоратор не работает должным образом с командой yield
Поскольку я очень часто использую один и тот же блок try-catch, я решил создать декоратор, который делает это автоматически.
def tryexc(func):
    @wraps(func)
    def wrapper(self, *args, **kwargs):
        try:
            func(self, *args, **kwargs)
        except Exception as e:
            mLib.log('EXCEPTION RAISED')
            mLib.log('ARGS:n'+'n'.join(str(x) for x in args))
            mLib.log(str(e))
            mLib.log(traceback.format_exc())
    return wrapper
class test_class():
    def __init__(self):
        self.text = 'TEST TEXT'
    @tryexc
    def x(self,a):
        print self.text 
        # yield self.text
    @tryexc
    def y(self,a):
        print list(self.x(5))
test_c = test_class()
test_c.y(5)
Когда yield self.text комментируется, все работает нормально. Текст напечатан. Но когда строка не комментируется, она ловит исключение.
    print list(self.x(5))
TypeError: 'NoneType' object is not iterable
1 ответ:
Давайте возьмем простой пример, чтобы объяснить вашу проблему -
Как вы можете видеть выше , функция>>> def tryexc(func): ... def wrapper(*args, **kwargs): ... try: ... func(*args, **kwargs) ... except Exception as e: ... print("Hmm", e) ... return wrapper ... >>> @tryexc ... def a(): ... return "Something" ... >>> a() >>>aдолжна возвращать'Something', но когда она была вызвана, она ничего не вернула. Почему?Поскольку при вызове оформленной функции сначала вызывается оболочка, а затем эта оболочка вызывает фактическую функцию, когда фактическая функция возвращает что-то, оболочка должна вернуть это. Но в вашем случае этого не происходит. Так вот почему ты здесь получение
Noneпри вызовеx()и это приводит к ошибкеNoneType.Теперь, чтобы исправить мой случай выше, я бы просто написал -
return func(*args, **kwargs)Но в вашем случае, если вы просто возвращаете, то произойдет то , что он вернет объект генератора , сгенерированный при вызове
func(), но если возникнет какое-либо исключение при вызове фактической функции (при итерации по объекту генератора), он не будет пойман вашим декоратором. Пример, чтобы показать, что -Это происходит потому, что как только оболочка возвращает объект генератора, этот поток заканчивается, и мы больше не находимся внутри него. Для вашего случая генератора, что вам действительно нужно сделать, это создать другой декоратор, который бы дал результаты от объекта генератора, возвращенного>>> @tryexc ... def a(): ... for i in range(10): ... yield i ... raise Exception('Hmm123') ... >>> list(a()) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 5, in a Exception: Hmm123func(). Пример / Демо -def tryexcgenerator(func): def wrapper(*args, **kwargs): try: for i in func(*args, **kwargs): yield i except Exception as e: print("Hmm", e) return wrapper >>> @tryexcgenerator ... def a(): ... for i in range(10): ... yield i ... raise Exception('Hmm123') ... >>> list(a()) Hmm Hmm123 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]Выше приведен только пример, вам нужно было бы использовать подобную логику для вашего декоратора.