装饰器不能与 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]

以上只是一个示例,您需要为装饰器使用类似的逻辑。