装饰生成器函数

Decorated generator function

我有一个装饰器:

def remediation_decorator(dec_mthd):
    def new_func(*args, **kwargs):
        try:
            return dec_mthd(*args, **kwargs)
        except (KeyError, HTTPError) as err:
            print(f'error = {err}... call the remediation function')
    return new_func

在生成器函数内部,调用另一个函数以在特定条件下引发特定异常:

def check(number):
    if number == 1:
        raise HTTPError
    if number == 2:
        raise KeyError

这个生成器函数被装饰成这样:

@remediation_decorator
def dec_mthd_b(number):
    check(number)
    for i in range(0,3):
        yield i+1

当检查函数引发异常时,装饰器的 except 没有命中。

[ins] In [16]: dec_mthd_b(1)
Out[16]: <generator object dec_mthd_b at 0x10e79cc80>

它看起来像这样,因为它是一个生成器函数 - 来自 Yield expressions:

When a generator function is called, it returns an iterator known as a generator.

(我想知道是否从字面上理解这个 'it returns the iterator first irrespective of other logic in the function',因此为什么 check() 不引发异常?)

并且,

By suspended, we mean that all local state is retained, including the current bindings of local variables, the instruction pointer, the internal evaluation stack, and the state of any exception handling.

我的理解正确吗?请问有人可以进一步解释吗?

你没看错。下面的代码将抛出异常。创建生成器时,不会执行任何操作。您需要获取下一个元素,因此从生成器中获取值,然后它会引发异常。

g = dec_mthd_b(1)
next(g) #throws httperror

实际上迭代就是这样完成的,我们重复调用 next 方法,直到抛出 IterationError 异常。

是的,你明白了。 @remediation_decorator 是 python 中装饰器的语法糖。我将使用详细 (?) 形式:

def dec_mthd_b(number):
    check(number)
    for i in range(0, 3):
        yield i + 1

dec_mthd_b = remediation_decorator(dec_mthd_b)

这条线是做什么的? remediation_decorator 是你的装饰器,它给你内部函数,在你的例子中是 new_func.

什么是new_func?它是一个普通函数,当你调用它时,它运行函数体。

return 来自 new_func 的是什么? dec_mthd(*args, **kwargs).

这里dec_mthd指向dec_mthd_b又是一个函数。但是当你调用它时,因为里面有dec_mthd_b has yield`关键字,它你返回生成器对象。

现在重点来了。你的内部函数的主体,这里 new_func,执行没有任何问题。你得到了你的生成器对象。没有出现错误...

# this is the result of calling inner function, which gives you the generator object
gen = dec_mthd_b(1)

# Here is where you're going to face the exceptions.
for i in gen:
    print(i)

for 循环会发生什么? Python 运行 dec_mthd_b 的正文。错误是从那里引发的...

所以为了捕获异常,你有两个选择,要么在 dec_mthd_b 中捕获它,要么在最后一个 for 循环中捕获它。