What is generator.throw() good for?

后端 未结 4 1574
被撕碎了的回忆
被撕碎了的回忆 2021-01-30 19:55

PEP 342 (Coroutines via Enhanced Generators) added a throw() method to generator objects, which allows the caller to raise an exception inside the generato

4条回答
  •  轻奢々
    轻奢々 (楼主)
    2021-01-30 20:28

    Let's say I use a generator to handle adding information to a database; I use this to store network-received information, and by using a generator I can do this efficiently whenever I actually receive data, and do other things otherwise.

    So, my generator first opens a database connection, and every time you send it something, it'll add a row:

    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)
    

    That is all fine and well; every time I .send() my data it'll insert a row.

    But what if my database is transactional? How do I signal this generator when to commit the data to the database? And when to abort the transaction? Moreover, it is holding an open connection to the database, maybe I sometimes want it to close that connection to reclaim resources.

    This is where the .throw() method comes in; with .throw() I can raise exceptions in that method to signal certain circumstances:

    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()
    

    The .close() method on a generator does essentially the same thing; it uses the GeneratorExit exception combined with .throw() to close a running generator.

    All this is an important underpinning of how coroutines work; coroutines are essentially generators, together with some additional syntax to make writing a coroutine easier and clearer. But under the hood they are still built on the same yielding, and sending. And when you are running multiple coroutines in parallel, you need a way to cleanly exit those coroutines if one of them has failed, just to name an example.

提交回复
热议问题