装饰器不能与 yield 命令一起正常工作
Decorator does not work properly with yield command
因为我经常使用同一个 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
它在大多数情况下都能正确运行,而不是在方法中使用 yield 时。
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
我不是很喜欢装饰器,所以如果有任何建议,我将不胜感激。 list(self.x(5)) 在我看来应该是 ['TEST TEXT']。
让我们举一个简单的例子来解释你的问题-
>>> 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
函数应该是 return 'Something'
,但是当它被调用时,它并没有 return 任何东西。为什么?
因为当你调用一个装饰函数时,包装器首先被调用,然后包装器调用实际函数,当实际函数 return 是什么东西时,包装器应该 return那。但在你的情况下,这并没有发生。所以这就是为什么在调用 x()
时得到 None
并导致 NoneType
错误的原因。
现在,为了解决我上面的问题,我会简单地写 -
return func(*args, **kwargs)
但是在你的情况下,如果你只是简单地做 return,将会发生的是它会 return 当你调用 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
这是因为一旦包装器 return 成为生成器对象,流程就结束了,我们不再在其中。对于您的生成器案例,您真正需要做的是创建一个不同的装饰器,它会从生成器对象 return 由 func()
编辑的结果中产生结果。 Example/Demo-
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]
以上只是一个示例,您需要为装饰器使用类似的逻辑。
因为我经常使用同一个 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
它在大多数情况下都能正确运行,而不是在方法中使用 yield 时。
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
我不是很喜欢装饰器,所以如果有任何建议,我将不胜感激。 list(self.x(5)) 在我看来应该是 ['TEST TEXT']。
让我们举一个简单的例子来解释你的问题-
>>> 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
函数应该是 return 'Something'
,但是当它被调用时,它并没有 return 任何东西。为什么?
因为当你调用一个装饰函数时,包装器首先被调用,然后包装器调用实际函数,当实际函数 return 是什么东西时,包装器应该 return那。但在你的情况下,这并没有发生。所以这就是为什么在调用 x()
时得到 None
并导致 NoneType
错误的原因。
现在,为了解决我上面的问题,我会简单地写 -
return func(*args, **kwargs)
但是在你的情况下,如果你只是简单地做 return,将会发生的是它会 return 当你调用 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
这是因为一旦包装器 return 成为生成器对象,流程就结束了,我们不再在其中。对于您的生成器案例,您真正需要做的是创建一个不同的装饰器,它会从生成器对象 return 由 func()
编辑的结果中产生结果。 Example/Demo-
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]
以上只是一个示例,您需要为装饰器使用类似的逻辑。