generator.throw()的优点是什么?
PEP 342(通过增强型生成器的协程 throw()
为生成器对象添加了一个throw()
方法,允许调用者在生成器内引发exception(就像yield
expression式抛出一样)。
我想知道这个function的用例是什么。
比方说,我使用生成器来处理添加信息到数据库; 我用这个来存储networking接收到的信息,并且通过使用一个生成器,我可以有效地做到这一点,只要我真正接收到数据,并且做其他事情。
所以,我的生成器首先打开一个数据库连接,并且每次发送它时,都会添加一行:
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()
我的数据,它会插入一行。
但是,如果我的数据库是事务性的呢? 如何向数据库提交数据时发信号? 何时放弃交易? 此外,它是持有一个开放的连接到数据库,也许我有时希望它closures连接回收资源。
这是.throw()
方法进来的地方; 与.throw()
我可以在该方法中引发exception来表示某些情况:
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
exception结合.throw()
来closures正在运行的生成器。
所有这些都是协程工作的重要基础。 协程本质上就是生成器,再加上一些额外的语法来使得编写协程变得简单和清晰。 但在引擎盖下,他们仍然build立在相同的屈服,并发送。 而当你同时运行多个协同程序时,如果其中一个程序失败,就需要一个干净地退出这个协程的方法,仅举一个例子。
在我看来, throw()
方法很有用,原因很多。
-
对称性:没有强有力的理由只能在调用者中处理exception情况,而不能在生成器function中处理。 (假设一个发生器从数据库读取值返回一个错误的值,并且假设只有调用者知道这个值是不好的。使用
throw()
方法,调用者可以向发生器发信号,告诉发生器有exception情况)如果发生器可以引发exception,则由调用者拦截,反向也应该是可能的。 -
灵活性:生成器函数可能有多个
yield
语句,调用者可能不知道生成器的内部状态。 通过抛出exception,可以将生成器重置为已知状态,或者实现更复杂的stream控制,而单独使用next()
,send()
,close()
会更麻烦。
询问用例可能会引起误解:对于每个用例,您都可以生成一个反例,而不需要使用throw()
方法,并且讨论会一直持续下去。
一个用例是在发生exception时在堆栈跟踪中包含关于生成器内部状态的信息 – 调用者无法看到的信息。
例如,假设我们有一个像下面这样的生成器:我们想要的内部状态是生成器的当前索引号:
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)
以下代码不足以触发额外的exception处理:
# Stack trace includes only: "ValueError: bad value" for item in gen_items(): if item == "bad": raise ValueError("bad value")
但是,下面的代码确实提供了内部状态:
# Stack trace also includes: "Exception: error during index: 4" gen = item_generator() for item in gen: if item == "bad": gen.throw(ValueError, "bad value")