首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >generator.throw()有什么好处?

generator.throw()有什么好处?
EN

Stack Overflow用户
提问于 2012-07-14 16:50:30
回答 4查看 11.6K关注 0票数 68

PEP 342 (通过增强型发电机进行协作)在生成器对象中添加了一个throw()方法,该方法允许调用方在生成器内引发异常(就像由yield表达式抛出的那样)。

我想知道这个特性的用例是什么。

EN

回答 4

Stack Overflow用户

发布于 2012-07-14 20:13:07

假设我使用生成器来处理向数据库添加信息;我使用它来存储网络接收到的信息,通过使用生成器,我可以在实际接收数据时高效地完成这个任务,而其他事情则不然。

因此,我的生成器首先打开一个数据库连接,每次发送它时,它都会添加一行:

代码语言:javascript
复制
def add_to_database(connection_string):
    db = mydatabaselibrary.connect(connection_string)
    cursor = db.cursor()
    while True:
        row = yield
        cursor.execute('INSERT INTO mytable VALUES(?, ?, ?)', row)

这一切都很好;每次我.send()我的数据时,它都会插入一行。

但是如果我的数据库是事务性的呢?当将数据提交到数据库时,如何向这个生成器发出信号?什么时候中止交易?此外,它保存着与数据库的开放连接,也许我有时希望它关闭该连接以回收资源。

这就是.throw()方法出现的原因;使用.throw(),我可以在该方法中引发异常,以指示某些情况:

代码语言:javascript
复制
def add_to_database(connection_string):
    db = mydatabaselibrary.connect(connection_string)
    cursor = db.cursor()
    try:
        while True:
            try:
                row = yield
                cursor.execute('INSERT INTO mytable VALUES(?, ?, ?)', row)
            except CommitException:
                cursor.execute('COMMIT')
            except AbortException:
                cursor.execute('ABORT')
    finally:
        cursor.execute('ABORT')
        db.close()

生成器上的.close()方法在本质上也是如此;它使用GeneratorExit异常和.throw()组合来关闭正在运行的生成器。

所有这一切都是协同工作方式的重要基础;协同机制本质上是生成器,加上一些额外的语法使编写协同线更容易、更清晰。但是在引擎盖下,它们仍然建立在同样的屈服和发送的基础上。当您并行运行多个协同线时,您需要一种方法来干净地退出这些协同线,如果其中之一失败了,请举一个例子。

票数 78
EN

Stack Overflow用户

发布于 2012-07-17 18:47:27

在我看来,throw()方法是有用的,有很多原因。

  1. 对称性:没有充分的理由只在调用者而不是在生成器功能中处理异常情况。(假设从数据库读取值的生成器返回一个坏值,并且假设只有调用方知道值是坏的。使用throw()方法,调用方可以向生成器发出必须纠正的异常情况的信号。)如果生成器能够引发异常,并被调用方截获,则也应该有可能发生相反的情况。
  2. 灵活性:生成器函数可能有多个yield语句,调用方可能不知道生成器的内部状态。通过抛出异常,就可以将生成器重置到已知的状态,或者实现更复杂的流控制,这对于单独使用next()send()close()来说要麻烦得多。

重置内部状态的示例:

代码语言:javascript
复制
def gen():
    try:
        yield 10
        print("State1")
        yield 20
        print("State2")
        yield 30
        print("State3")
    
   except:
        #Reset back to State1!
        yield gen()

g = gen()
print(next(g))
print(next(g))
g = g.throw(ValueError) #state of g has been reset
print(next(g))

>>10
>>State1
>>20
>>10

询问用例可能会使人产生误解:对于每个用例,您可以生成一个反示例,而不需要throw()方法,而且讨论将永远持续下去.

票数 15
EN

Stack Overflow用户

发布于 2014-12-06 19:45:49

一个用例是在发生异常时,在堆栈跟踪中包含有关生成器内部状态的信息--这些信息对调用者来说是不可见的。

例如,假设我们有一个生成器,如下所示,我们想要的内部状态是生成器的当前索引号:

代码语言:javascript
复制
def gen_items():
    for i, item in enumerate(["", "foo", "", "foo", "bad"]):
        if not item:
            continue
        try:
            yield item
        except Exception:
            raise Exception("error during index: %d" % i)

以下代码不足以触发额外的异常处理:

代码语言:javascript
复制
# Stack trace includes only: "ValueError: bad value"
for item in gen_items():
    if item == "bad":
        raise ValueError("bad value")

但是,以下代码确实提供了内部状态:

代码语言:javascript
复制
# Stack trace also includes: "Exception: error during index: 4"
gen = item_generator()
for item in gen:
    if item == "bad":
        gen.throw(ValueError, "bad value")
票数 9
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/11485591

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档