Decorator不能与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

它在大多数情况下都是正确的,而不是在方法中使用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函数应该返回'Something',但是当它被调用时,它没有返回任何东西。为什么?

因为当你调用一个修饰函数时,包装器首先被调用,然后包装器调用实际函数,当实际函数返回一些东西时,包装器应该返回那个。但在你的情况下,这种情况不会发生。所以这就是为什么当x()被调用时你得到None,这导致NoneType错误。

现在,为了解决上面的问题,我将简单地写入-

return func(*args, **kwargs)

但是在您的情况下,如果您只是简单地执行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

这是因为一旦包装器返回生成器对象,流就结束了,我们不再在里面了。对于生成器的情况,您真正需要做的是创建一个不同的装饰器,它将产生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]

上面只是一个例子,你需要为你的装饰器使用类似的逻辑。

最新更新