Декоратор не работает должным образом с командой 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
Я не очень люблю декораторов, поэтому буду признателен за любой совет. список (self.x (5)) должно быть ['тест Текст'] на мой взгляд.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: Hmm123
func()
. Пример / Демо -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]
Выше приведен только пример, вам нужно было бы использовать подобную логику для вашего декоратора.