Python 協程



Python **協程**是程式設計中的一個基本概念,它擴充套件了傳統函式的功能。它們在非同步程式設計和複雜資料處理流水線中特別有用。

**協程**是函式和生成器概念的擴充套件。它們旨在執行協作式多工處理和管理非同步操作。

傳統函式(即子程式)只有一個入口點和一個出口點,而協程可以在多個點暫停和恢復執行,這使得它們非常靈活。

協程的關鍵特性

以下是 Python 中協程的關鍵特性:

  • **多個入口點:**協程不像傳統函式那樣只受限於單個入口點。它們可以在遇到 `yield` 語句時暫停執行,並在稍後恢復。這允許協程處理涉及等待或處理非同步資料的複雜工作流程。
  • **沒有中央協調器:**傳統函式(即子程式)通常由主函式協調,但協程更獨立地執行。它們可以以流水線的方式相互互動,資料流經一系列協程,每個協程執行不同的任務。
  • **協作式多工處理:**協程支援協作式多工處理。這意味著它不是依賴於作業系統或執行時在任務之間切換,而是由程式設計師控制協程何時讓出和恢復,從而允許對執行流程進行更細粒度的控制。

子程式與協程

**子程式**是具有單個入口點且沒有暫停或恢復執行的固有機制的傳統函式。它們按定義的順序呼叫,並處理具有直接控制流的任務。

**協程**是具有多個入口點的更高階函式,可以暫停和恢復執行。它們對於需要非同步執行、複雜控制流和資料流水線的任務很有用。它們透過允許程式設計師控制任務之間何時切換執行來支援協作式多工處理。

下表有助於理解子程式和協程之間的關鍵區別和相似之處,使我們更容易掌握它們在程式設計中的各自作用和功能。

標準 子程式 協程
定義 執行任務的一系列指令。 可以暫停和恢復執行的子程式的泛化。
入口點 單個入口點。 多個入口點;可以暫停和恢復執行。
執行控制 由主函式或控制結構呼叫。 可以暫停執行並在以後恢復,程式設計師控制切換。
用途 執行特定任務或計算。 管理非同步操作、協作式多工處理和複雜工作流程。
呼叫機制 通常由主函式或其他子程式呼叫。 使用`next()`、`send()`和`close()`方法呼叫和控制。
資料處理 沒有內建的資料交換機制;通常使用引數和返回值。 可以使用帶有`send()`的`yield`接收和處理資料。
狀態管理 沒有固有的機制來維護呼叫之間的狀態。 在掛起之間維護執行狀態,並可以從中斷處恢復。
用法 這些用於將程式碼模組化成可管理的塊。 這些用於非同步程式設計、管理資料管道和協作式多工處理。
併發 並非天生設計用於併發執行;通常用於順序程式設計。 支援協作式多工處理,並可以與非同步任務一起工作。
示例用法 輔助函式,實用函式。 資料管道,非同步任務,協作式多工處理。
控制流 執行遵循程式碼中的線性路徑。 執行可以根據yield點在協程之間來回跳轉。

協程的執行

協程__next__()方法啟動,該方法啟動協程並將執行推進到第一個yield語句。然後,協程等待向其傳送值。send()方法用於向協程傳送值,協程可以處理這些值並可能產生結果。

基本協程示例

協程使用yield語句,該語句可以傳送和接收值。與生成器不同,生成器產生用於迭代的值,而協程通常使用yield接收輸入並根據該輸入執行操作。以下是Python協程的基本示例:

def print_name(prefix):
    print(f"Searching prefix: {prefix}")
    while True:
        name = (yield)
        if prefix in name:
            print(name)

# Instantiate the coroutine
corou = print_name("Welcome to")

# Start the coroutine
corou.__next__()

# Send values to the coroutine
corou.send("Tutorialspoint")
corou.send("Welcome to Tutorialspoint")

輸出

Searching prefix: Welcome to
Welcome to Tutorialspoint

關閉協程

協程可以無限期執行,因此在不再需要它們時正確關閉它們非常重要。close()方法終止協程並處理清理工作。如果我們嘗試向已關閉的協程傳送資料,它將引發StopIteration異常

示例

以下是 Python 中關閉協程的示例:

def print_name(prefix):
    print(f"Searching prefix: {prefix}")
    try:
        while True:
            name = (yield)
            if prefix in name:
                print(name)
    except GeneratorExit:
        print("Closing coroutine!!")

# Instantiate and start the coroutine
corou = print_name("Come")
corou.__next__()

# Send values to the coroutine
corou.send("Come back Thank You")
corou.send("Thank you")

# Close the coroutine
corou.close()

輸出

Searching prefix: Come
Come back Thank You
Closing coroutine!!

將協程連結起來形成管道

協程可以連結在一起形成處理管道,允許資料流經一系列階段。這對於分階段處理資料序列特別有用,其中每個階段執行特定任務。

示例

以下示例顯示了將協程連結起來形成管道的過程:

def producer(sentence, next_coroutine):
   '''
   Splits the input sentence into tokens and sends them to the next coroutine.
   '''
   tokens = sentence.split(" ")
   for token in tokens:
      next_coroutine.send(token)
   next_coroutine.close()

def pattern_filter(pattern="ing", next_coroutine=None):
   '''
   Filters tokens based on the specified pattern and sends matching tokens to the next coroutine.
   '''
   print(f"Searching for {pattern}")
   try:
      while True:
         token = (yield)
         if pattern in token:
            next_coroutine.send(token)
   except GeneratorExit:
      print("Done with filtering!!")
      next_coroutine.close()

def print_token():
   '''
   Receives tokens and prints them.
   '''
   print("I'm the sink, I'll print tokens")
   try:
      while True:
         token = (yield)
         print(token)
   except GeneratorExit:
      print("Done with printing!")

# Setting up the pipeline
pt = print_token()
pt.__next__()

pf = pattern_filter(next_coroutine=pt)
pf.__next__()

sentence = "Tutorialspoint is welcoming you to learn and succeed in Career!!!"
producer(sentence, pf)

輸出

I'm the sink, I'll print tokens
Searching for ing
welcoming
Done with filtering!!
Done with printing!
廣告