<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                ## 問題 當你編寫使用回調函數的代碼的時候,擔心很多小函數的擴張可能會弄亂程序控制流。你希望找到某個方法來讓代碼看上去更像是一個普通的執行序列。 ## 解決方案 通過使用生成器和協程可以使得回調函數內聯在某個函數中。為了演示說明,假設你有如下所示的一個執行某種計算任務然后調用一個回調函數的函數(參考7.10小節): def apply_async(func, args, *, callback): # Compute the result result = func(*args) # Invoke the callback with the result callback(result) 接下來讓我們看一下下面的代碼,它包含了一個 `Async` 類和一個 `inlined_async` 裝飾器: from queue import Queue from functools import wraps class Async: def __init__(self, func, args): self.func = func self.args = args def inlined_async(func): @wraps(func) def wrapper(*args): f = func(*args) result_queue = Queue() result_queue.put(None) while True: result = result_queue.get() try: a = f.send(result) apply_async(a.func, a.args, callback=result_queue.put) except StopIteration: break return wrapper 這兩個代碼片段允許你使用 `yield` 語句內聯回調步驟。比如: def add(x, y): return x + y @inlined_async def test(): r = yield Async(add, (2, 3)) print(r) r = yield Async(add, ('hello', 'world')) print(r) for n in range(10): r = yield Async(add, (n, n)) print(r) print('Goodbye') 如果你調用 `test()` ,你會得到類似如下的輸出: 5 helloworld 0 2 4 6 8 10 12 14 16 18 Goodbye 你會發現,除了那個特別的裝飾器和 `yield` 語句外,其他地方并沒有出現任何的回調函數(其實是在后臺定義的)。 ## 討論 本小節會實實在在的測試你關于回調函數、生成器和控制流的知識。 首先,在需要使用到回調的代碼中,關鍵點在于當前計算工作會掛起并在將來的某個時候重啟(比如異步執行)。當計算重啟時,回調函數被調用來繼續處理結果。`apply_async()` 函數演示了執行回調的實際邏輯,盡管實際情況中它可能會更加復雜(包括線程、進程、事件處理器等等)。 計算的暫停與重啟思路跟生成器函數的執行模型不謀而合。具體來講,`yield` 操作會使一個生成器函數產生一個值并暫停。接下來調用生成器的 `__next__()` 或 `send()` 方法又會讓它從暫停處繼續執行。 根據這個思路,這一小節的核心就在 `inline_async()` 裝飾器函數中了。關鍵點就是,裝飾器會逐步遍歷生成器函數的所有 `yield` 語句,每一次一個。為了這樣做,剛開始的時候創建了一個 `result` 隊列并向里面放入一個 `None` 值。然后開始一個循環操作,從隊列中取出結果值并發送給生成器,它會持續到下一個 `yield` 語句,在這里一個 `Async` 的實例被接受到。然后循環開始檢查函數和參數,并開始進行異步計算 `apply_async()` 。然而,這個計算有個最詭異部分是它并沒有使用一個普通的回調函數,而是用隊列的 `put()` 方法來回調。 這時候,是時候詳細解釋下到底發生了什么了。主循環立即返回頂部并在隊列上執行 `get()` 操作。如果數據存在,它一定是 `put()` 回調存放的結果。如果沒有數據,那么先暫停操作并等待結果的到來。這個具體怎樣實現是由 `apply_async()` 函數來決定的。如果你不相信會有這么神奇的事情,你可以使用 `multiprocessing` 庫來試一下,在單獨的進程中執行異步計算操作,如下所示: if __name__ == '__main__': import multiprocessing pool = multiprocessing.Pool() apply_async = pool.apply_async # Run the test function test() 實際上你會發現這個真的就是這樣的,但是要解釋清楚具體的控制流得需要點時間了。 將復雜的控制流隱藏到生成器函數背后的例子在標準庫和第三方包中都能看到。比如,在``contextlib`` 中的 `@contextmanager` 裝飾器使用了一個令人費解的技巧,通過一個 `yield` 語句將進入和離開上下文管理器粘合在一起。另外非常流行的 `Twisted` 包中也包含了非常類似的內聯回調。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看