亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Python協程原理全面分析

 更新時間:2023年02月08日 10:46:30   作者:lijiachang8  
協程(co-routine,又稱微線程、纖程)是一種多方協同的工作方式。協程不是進程或線程,其執(zhí)行過程類似于Python函數調用,Python的asyncio模塊實現的異步IO編程框架中,協程是對使用async關鍵字定義的異步函數的調用

序章

yield item這行代碼會產出一個值,提供給next()的調用方;此外還會做出讓步,暫停執(zhí)行生成器,讓調用方繼續(xù)工作,知道需要使用另一個值再調用next()。調用方會從生成器中拉取值。

從語法上來看,協程與生成器類似,都是從定義體中包含yield關鍵字的函數??墒?,在協程中,yield通常出現在表達式的右邊(如data = yield),可以產出值,也可以不產出:如果yield關鍵字后面沒有表達式,那么生成器產出None。協程可能會從調用方接收數據,不過調用方把提供數據給協程使用的方式是.send(data)方法。調用方會把值推送給協程。

yield關鍵字甚至可以不接受或傳出數據。不管數據如何流動,yield都是一種流程控制工具,使用它可以實現協作式多任務;協程可以把控制器讓步給中心調度程序,從而激活其他的協程。

綜上,如果從根本上把yield視為控制流程的方式,這樣就好理解協程了

協程可以認為是可以明確標記有某種語法元素(yield from)的階段“暫停”函數

生成器如何進化為協程

協程的框架是在Python2.5(2006年)實現的。自此之后,yield關鍵字可以在表達式中使用,并且生成器增加了.send(value)方法。生成器的調用方法可以使用.send()發(fā)送數據,發(fā)送的數據會成為生成器中yield表達式的值。因此生成器可以作為協程使用。協程是指一個過程,這個過程與調用方協作,產出調用方法提供的值。

除了.send()方法,后續(xù)還增加了.throw()和.close()方法:前者作用是讓調用方拋出異常,在生成器中處理;后者作用是終止生成器。

用作協程的生成器的基本行為

示例,一個簡單的協程

def simple_coroutine():
    print('-> coroutine started')
    x = yield
    print('-> coroutine received:', x)
my_coro = simple_coroutine()
print(my_coro)  # 得到的是生成器對象
print(next(my_coro))  # None
print(my_coro.send(42))
打印
<generator object simple_coroutine at 0x00D957B0>
-> coroutine started
None
-> coroutine received: 42
Traceback (most recent call last):
  File "C:/Users/lijiachang/PycharmProjects/collect_demo/test2.py", line 13, in <module>
    print(my_coro.send(42))
StopIteration

知識點:

  • 定義體中x = yield 這種形式,如果協程只需要從客戶那接收數據,那么產出值是None,所以next(my_coro)的值為None,這個值是隱式指定的,因為yield關鍵字右邊沒有表達式。
  • 首先要調用next(my_coro),因為生成器還沒有啟動,沒在yield語句處暫停,所以一開始是無法發(fā)送數據的,首先要調用next(),到yield關鍵字處暫停。
  • 當執(zhí)行到my_coro.send(42),協程定義體中的yield表達式會得到42,然后賦值給x;協程繼續(xù)運行到下一個yield表達式,或者終止。
  • 最后,控制權流動到協程定義體的末尾,導致生成器像往常一樣拋出StopIteration異常

協程的四個狀態(tài)

協程可以身處四個狀態(tài)中的一個。獲取當前狀態(tài)可以使用inspect.getgeneratorstate()函數確定,該函數會返回下面四種狀態(tài)的一個:

  • 'GEN_CREATED' :等待開始執(zhí)行
  • 'GEN_RUNNING' :解釋器正在執(zhí)行 (只有在多線程程序才能看到這個狀態(tài))
  • 'GEN_SUSPENDED' :在yield表達式處暫停
  • 'GEN_CLOSED' : 執(zhí)行結束

因為send方法的參數會成為暫停的yield表達式的值,所以僅當協程處于暫停狀態(tài)時才能調用send方法,例如my_coro.send(42).。

不過,如果協程還沒激活(即狀態(tài)為GEN_CREATED),始終要調用next(my_coro)激活協程----也可以調用my_coro.send(None)激活。

ps:如果創(chuàng)建協程對象后立即把None之外的值發(fā)給它,會出現以下錯誤,清晰明了:TypeError: can't send non-None value to a just-started generator

最先調用next(my_coro)函數這一步通常稱為“預激”(prime)協程,即讓協程向前執(zhí)行到第一個yield表達式,準備好作為活躍的協程使用。

from inspect import getgeneratorstate
def simple_coro2(a):
    print('-> started: a=', a)
    b = yield a
    print('->  received: b=', b)
    c = yield a + b
    print('->  received: c=', c)
my_coro2 = simple_coro2(14)
print(getgeneratorstate(my_coro2))
print(next(my_coro2))
print(getgeneratorstate(my_coro2))
print(my_coro2.send(28))
print(my_coro2.send(99))
打印
GEN_CREATED
-> started: a= 14
14
GEN_SUSPENDED
->  received: b= 28
42
->  received: c= 99
Traceback (most recent call last):
  File "C:/Users/lijiachang/PycharmProjects/collect_demo/test2.py", line 19, in <module>
    print(my_coro2.send(99))
StopIteration

知識點:

  • 協程在yield關鍵字所在的位置暫停執(zhí)行。
  • 在賦值語句中,=右邊的代碼在賦值前執(zhí)行。因此對于b = yield a這行代碼,等到客戶端代碼再激活協程時才會設定b的值。

示例-使用協程計算平均值

示例,一個累積計算平均值的函數,使用協程實現

def averager():
    total = 0.0
    count = 0
    average = 0
    while True:
        num = yield average
        total += num
        count += 1
        average = total / count
aver = averager()
print(aver.send(None))  # 預激協程 
print(aver.send(2))
print(aver.send(4))
print(aver.send(6))

打印
0
2.0
3.0
4.0

知識點:

  • 使用協程之前,需要預激協程,除了使用aver.send(None)還可以使用next(aver)
  • 這個示例中,是個無限循環(huán)的,僅當調用方在協程上調用.close()方法,或者沒有對協程的引用而被垃圾回收程序回收時,這個協程才會終止。

預激協程的裝飾器

如果不預激,那么協程沒什么用。

調用my_coro.send(x)之前,一定要先調用next(my_coro)。為了簡化協程的用法,有時候會使用一個預激裝飾器。

from inspect import getgeneratorstate
from functools import wraps
def coroutine(func):
    """預激協程的裝飾器,向前執(zhí)行到第一個yield表達式"""
    @wraps(func)
    def primer(*args, **kwargs):
        gen = func(*args, **kwargs)
        next(gen)
        return gen
    return primer
@coroutine
def averager():
    total = 0.0
    count = 0
    average = 0
    while True:
        num = yield average
        total += num
        count += 1
        average = total / count
aver = averager()
print(getgeneratorstate(aver))
print(aver.send(2))
print(aver.send(4))
print(aver.send(6))

打印
GEN_SUSPENDED
2.0
3.0
4.0

可以看到,使用預激裝飾器裝飾了averager函數上之后,協助立即處于GEN_SUSPENED狀態(tài),因此這個協程已經準備好,可以接收值了。

很多框架都提供了處理協程的特殊裝飾器,不過不是所有裝飾器都用于預激協程,有些會提供其他服務,例如勾入事件循環(huán)。

使用yeild from句法調用協程時,會自動預激,因此使用@coroutine等裝飾器不兼容。Python3.4標準庫中的asyncio.coroutine裝飾器不會預激協程,因此能兼容yeild from句法。

終止協程和異常處理

協程中未處理的異常會向上冒泡,傳給觸發(fā)協程的對象(next函數或send函數的調用方)。

未處理的異常會導致協程終止:

In [4]: aver.send(20)

Out[4]: 20.0

In [5]: aver.send(30)

Out[5]: 25.0

In [6]: aver.send('spam')

---------------------------------------------------------------------------

TypeError Traceback (most recent call last)

TypeError: unsupported operand type(s) for +=: 'float' and 'str'

In [7]: aver.send(40)

---------------------------------------------------------------------------

StopIteration Traceback (most recent call last)

StopIteration:

以上可以聯想到,終止協程的一種方式:發(fā)送某個哨符值,讓協程退出。

比如內置的None和Ellipsis(省略號...)常量經常用作哨符值。Ellipsis的優(yōu)點是數據流中不太常有這個值。也有人把StopIteration作為哨符值,這樣:my_coro.send(StopIteration)

從Python2.5開始,可以在生成器對象調用兩個方法,顯式的把異常發(fā)給協程。

這兩個方法是throw拋和close:

generator.throw(exc_type[, exc_value[, traceback]])

使生成器在yeild表達式處拋出指定的異常。

如果生成器處理了拋出的異常,代碼會向前執(zhí)行到下一個yeild表達式處,而產生的值會成為調用generator.throw方法得到的返回值。

如果生成器沒有處理拋出的異常,異常會向上冒泡,傳給調用方的上下文中。

generator.close()

使生成器在暫停的yield表達式處拋出GeneratorExit異常。

如果生成器沒有處理這個異常,或者拋出了StopIteration異常(表示運行到結尾),調用方不會報錯。

如果收到GeneratorExit異常,生成器一定不能產出值,否則解釋器會拋出RuntimeError異常。

生成器拋出的其他異常會向上冒泡,傳給調用方。

示例,使用close和throw方法控制協程。

from inspect import getgeneratorstate
class DemoException(Exception):
    pass
def demo_exc_handling():
    """異常處理demo"""
    print('-> coroutine started')
    while True:
        try:
            x = yield
        except DemoException:
            print('*** DemoException handled')  # 處理異常
        else:
            print('-> coroutine received: {!r}'.format(x))  # 如果沒有異常,顯示接收的值
exc_coro = demo_exc_handling()
next(exc_coro)
exc_coro.send(11)
exc_coro.close()  # 正常關閉協程
print(getgeneratorstate(exc_coro))
exc_coro2 = demo_exc_handling()
next(exc_coro2)
exc_coro2.throw(DemoException)  # 把異常傳入協程后,如果有處理,不會終止協程
exc_coro2.send(22)
print(getgeneratorstate(exc_coro2))
exc_coro2.throw(ZeroDivisionError)  # 如果把無法處理的異常傳入,協程會終止
打印
-> coroutine started
-> coroutine received: 11
GEN_CLOSED
-> coroutine started
*** DemoException handled
-> coroutine received: 22
GEN_SUSPENDED
Traceback (most recent call last):
  File "C:/Users/lijiachang/PycharmProjects/collect_demo/test2.py", line 33, in <module>
    exc_coro2.throw(ZeroDivisionError)
  File "C:/Users/lijiachang/PycharmProjects/collect_demo/test2.py", line 13, in demo_exc_handling
    x = yield
ZeroDivisionError

知識點:

  • 使用.close()關閉協程,沒有任何異常拋出
  • 如果協程中有對應的異常處理代碼,.throw()的異常不會終止協程。如果沒有異常處理,協程會終止

如果不管協程如何結束都想要做些清理工作的話,要把協程定義體匯總相關的代碼放入try/finally塊中

def demo_exc_handling():
    """異常處理demo"""
    print('-> coroutine started')
    try:
        while True:
            try:
                x = yield
            except DemoException:
                print('*** DemoException handled')  # 處理異常
            else:
                print('-> coroutine received: {!r}'.format(x))  # 如果沒有異常,顯示接收的值
    finally:
        print('-> coroutine ending.')
        print('do something.')

獲取協程返回值

下面是averager累積求平均數的另一個版本,這個版本不會隨著增加元素返回平均值,而是最后返回一個值。

from collections import namedtuple
from functools import wraps
def coroutine(func):
    """預激協程裝飾器"""
    @wraps(func)
    def prime(*args, **kwargs):
        gen = func(*args, **kwargs)
        next(gen)
        return gen
    return prime
Result = namedtuple('Result', 'count average')
@coroutine
def averager():
    count = 0
    average = 0.0
    total = 0
    while True:
        item = yield
        if item is None:
            break  # 為了獲取返回值,協程必須正常終止,因此要有個判斷,以便退出累計循環(huán)
        count += 1
        total += item
        average = total / count
    return Result(count, average)  # 最終返回一個namedtuple,包含信息
ave = averager()
ave.send(2)
ave.send(4)
print(ave.send(None))
打印
Traceback (most recent call last):
  File "C:/Users/lijiachang/PycharmProjects/collect_demo/test2.py", line 39, in <module>
    print(ave.send(None))
StopIteration: Result(count=2, average=3.0)

知識點:

  • 使用ave.send(None)發(fā)送None終止循環(huán),或者使用next(ave)也可以。結果就是導致協程結束,返回結果
  • 代碼中break跳出了while循環(huán),導致運行到定義體結束,也就拋出StopIteration很正常。
  • 異常對象的value保存著return返回的值。return表達式的值會偷偷傳給調用方,賦值給StopIteration異常的一個屬性。這樣做有點不合常理,但是能保留生成器對象的常規(guī)行為:耗盡時拋出StopIteration異常。

既然協程定義體中return返回值,是寄托到了異常的value值中,那么就捕獲異常:

try:
    print(ave.send(None))
except StopIteration as e:
    result = e.value
print(result)
打印
Result(count=2, average=3.0)

獲取協程的返回值雖然要繞個圈子,但是這是PEP 380定義的方式。

但是我們平時不需要這樣做,因為yield from結構會在內部自動捕獲StopIteration異常。這種處理方式和for循環(huán)處理StopIteration異常的方式一樣:循環(huán)機制會讓給用戶易于理解的方式處理異常。

而且,對yeild from結構來說,不僅可以捕獲StopIteration異常,還會把異常的value屬性值變?yōu)閥eild from的值。

所以接下來要介紹yield from結構

使用yield from

要知道yield from是全新的語言結構。它的作用比yield多得多,因此人們認為繼續(xù)使用yield 關鍵字多少會引起誤解。

在Python3.4之后,被await關鍵字代替。

在生成器gen中使用yeild from subgen()時,subgen會獲得控制權,把產出的值傳給gen的調用方,即調用方直接可以控制subgen來得到產出值。以此同時,gen會阻塞,帶燈subgen終止。

yield from x表達式對x對象所做的第一件事是,調用iter(x),從中獲取迭代器。因此x是任何可迭代對象。

yield from 結構不是簡單的替代產出值的嵌套for循環(huán),而是把職責委托給子生成器的句法。

yield from 的主要功能是打開雙向通道,把最外層的調用方和最內層的子生成器連接起來,

這樣二者可以直接發(fā)送和產出值,還可以直接傳入異常,而不用在位于中間的協程中添加大量打異常處理。有了這個結構,協程可以通過以前不可能的方式委托職責。

相關的專業(yè)術語:

委派生成器

包含yield from <iterable> 表達式的生成器函數。

子生成器

從yield from表達式中<iterable>部分獲取的生成器。

調用方

指代調用委派生成器的客戶端代碼。

委派生成器在yield from表達式處暫停時,調用方可以直接把數據發(fā)給子生成器,子生成器再把產生的值發(fā)給調用方。子生成器返回之后,解釋器會拋出StopIteration異常,并把返回值附加到異常對象上(異常的value屬性),此時委派生成器會恢復。

下面的示例,用于說明yield from結構的用法

from collections import namedtuple
from functools import wraps
Result = namedtuple('Result', 'count average')
# 作為子生成器使用
def averager():
    count = 0
    average = 0.0
    total = 0
    while True:
        item = yield
        if item is None:
            break
        count += 1
        total += item
        average = total / count
    return Result(count, average)  # 返回的結果最后會成為grouper函數中的yield from中的值
# 委派生成器
def grouper(results, key):
    while True:  # 每次循環(huán)都會產生一個新的averager實例,每個實例都作為協程使用的生成器對象
        results[key] = yield from averager()  # grouper每次接受的到值都通過yield from處理,通過管道傳給averager實例
# 客戶端代碼,即調用方
def main(data):
    results = {}
    for key, values in data.items():
        group = grouper(results, key)  # group作為協程使用
        next(group)  # 預激協程
        for value in values:
            group.send(value)  # 把value傳給grouper,最終到達的是averager函數的item = yield那行。
        group.send(None)  # 把None傳給grouper,讓當前averager實例終止,讓grouper繼續(xù)運行。
    print(results)
data = {
    'girls;kg': [40.9, 38.5, 44.3, 42.2, 45.2, 41.7, 44.5, 38.0, 40.6, 44.5],
    'girls;m': [1.6, 1.51, 1.4, 1.3, 1.41, 1.39, 1.33, 1.46, 1.45, 1.43],
    'boys;kg': [39.0, 40.8, 43.2, 40.8, 43.1, 38.6, 41.4, 40.6, 36.3],
    'boys;m': [1.38, 1.5, 1.32, 1.25, 1.37, 1.48, 1.25, 1.49, 1.46],
}
if __name__ == "__main__":
    main(data)

知識點:

  • grouper會在yield from表達式處暫停,等待averager實例處理客戶端發(fā)來的值。averager實例運行完畢后,返回的值綁定到了results[key]上,while循環(huán)會不斷創(chuàng)建averager實例,處理更多的值
  • 對于委派生成器grouper來說,永遠不知道傳入的值是什么。因為當value值傳給grouper,最終到達的是averager函數的item = yield那行。

group.send(None) 的作用非常重要,它讓當前的averager實例終止,然后在創(chuàng)建一個新的averager實例。如果沒有它,results結果中不會有任何內容。

下面說明沒有group.send(None) 時,最終的運作方式:

  • 外層for循環(huán)每次迭代會新建一個grouper實例,賦值給group變量;group是委派生成器
  • 調用next(group),是預激委派生成器grouper,此時進入while Ture循環(huán),調用子生成器averager后,在yield from表達式處暫停
  • 內層for循環(huán)調用group.send(value),直接把值傳給子生成器averager。同時,當前的grouper實例(group)在yield from表達式處暫停。
  • 內層for循環(huán)結束后,group實例依舊在yield from表達式處暫停,因此,grouper函數定義體中為results[key]賦值的語句還沒有執(zhí)行。
  • 如果外層for循環(huán)的末尾沒有group.send(None),那么averager子生成器永遠不會終止,委派生成器group永遠不會再次激活,因此永遠不會為results[key]賦值
  • 外層for循環(huán)重新迭代時會新建一個grouper實例,然后綁定到group變量上。前一個grouper實例以及它創(chuàng)建的未終止的averager子生成器實例,會被垃圾回收程序回收。

上面想說明的關鍵一點是,如果子生成器不終止,委派生成器會在yield from表達式處永遠暫停。

如果這樣,程序不會向前進行,因為yield from把控制權交給了客戶端代碼(即委派生成器的調用方)。

以上的示例和說明,展示了yield from結構最簡單的用法,只有委派生成器和一個子生成器。因為委派生成器相當于管道,所以可以把任意數量個委派生成器連接在一起:一個委派生成器使用yield from調用一個子生成器,而那個子生成器本身也是委派生成器,使用yield from調用另一個生成器,以此類推。最終,這個鏈條要以一個只使用yield 表達式的簡單生成器結束,或者以任何可迭代對象結束。

任何yield from鏈條都必須由客戶驅動,在最外層委派生成器上調用next()函數或.send()方法??梢杂胒or循環(huán)等隱式調用。

yield from的意義

在PEP 380中有這么一段話,yield from的作者Greg Ewing的草稿,可以粗略的解釋:

把迭代器當作生成器使用,相當于 把子生成器的定義體內聯在yield from表達式中。此外,子生成器可以執(zhí)行return語句,返回一個值,而返回的值會成為yield from表達式的值。

批準后的PEP 380,分六點說明了yield from的行為:

  • 子生成器產出的值都直接傳給委派生成器的調用方(即客戶端代碼)
  • 使用send()方法發(fā)送給委派生成器的值都直接傳給子生成器。如果發(fā)送的值是None,那么會調用子生成器的__next__()方法。如果發(fā)送的值不是None,那么會調用子生成器的send()方法。如果調用的方法拋出StopIteration異常,那么委派生成器會恢復運行。任何的其他異常會向上冒泡,傳給委派生成器。
  • 生成器退出時,生成器或者子生成器中的return expr表達式會觸發(fā)StopIteration(expr)異常拋出。
  • yield from表達式的值,是子生成器終止時傳給StopIteration異常的第一個參數。
  • 傳入委派生成器的異常,除了GeneratorExit之外都傳給子生成器的throw()方法。如果調用throw()方法時拋出StopIteration異常,委派生成器恢復運行。StopIteration之外的異常會向上冒泡,傳給委派生成器。
  • 如果把GeneratorExit異常傳給委派生成器,或者在委派生成器上調用close()方法,那么會在子生成器上調用close()方法(前提是有此方法)。如果調用close()方法導致異常拋出,那么異常會向上冒泡,傳給委派生成器;否則,委派生成器拋出GeneratorExit異常。

yield from的具體語義難以理解,尤其是最后兩點。

Greg Ewing還使用了偽代碼,演示了yield from的行為。下面是把原40行的偽代碼簡化了的邏輯,因為原40行代碼難以理解。假設了客戶端代碼有在委派生成器上調用.throw()和.close()方法,假設子生成器不會拋出異常,而是一直運行到終止,讓解釋器拋出StopIteration異常。

下面列出的偽代碼,是對這行代碼的擴充:RESULT = yield from EXPR

"""RESULT = yield from EXPR 的偽代碼"""
_i = iter(EXPR)  # EXPR 為任何可迭代對象,獲取迭代器_i使用iter()函數,這里是獲取子生成器
try:
    _y = next(_i)  # 預激子生成器,把結果保存在_y中,作為產出的第一個值
except StopIteration as _e:
    _r = _e.value  # 如果拋出StopIteration異常,獲取異常對象中的value屬性,賦值給_r 這是最簡單情況下的返回值
else:
    while 1: # 運行這個循環(huán)時,委派生成器會阻塞,值作為調用方和子生成器之間的通道
        _s = yield _y  # 產出子生成器當前產出的元素;等待調用方發(fā)送_s保存的值。
        try:
            _y = _i.send(_s)  # 嘗試讓子生成器向前執(zhí)行,轉發(fā)調用方發(fā)送的_s
        except StopIteration as _e: # 如果子生成器拋出StopIteration異常,獲取異常對象中的value屬性,賦值給_r;然后退出循環(huán),委派生成器恢復運行
            _r = _e.value
            break
RESULT = _r # 返回的結果是_r,即是整個yield from表達式的值

RESULT = _r # 返回的結果是_r,即是整個yield from表達式的值

  • _i : 迭代器,子生成器
  • _y : 產出的值,子生成器產出的值
  • _r : 結果,最終的結果,即子生成器運行結束后yield from表達式的值
  • _s : 發(fā)送的值,調用方發(fā)給委派生成器的值,這個值會轉發(fā)給子生成器
  • _e : 異常對象

但是,現實情況會復雜一些,因為客戶要對.throw()和.close()方法調用,二者兩個方法的執(zhí)行操作,必須傳入子生成器。

子生成器可能只是純粹的迭代器,不支持.throw和.close()方法,因此yield from結構邏輯必須處理這種情況。

如果子生成器實現了這兩個方法,而在子生成器內部,這兩個方法都會觸發(fā)異常拋出,這種情況也必須由yield from機制處理。

調用方可能會無緣無故的讓子生成器自己拋出異常,實現yield from結構時要處理這種情況。

最后,為了優(yōu)化,如果調用方調用next函數或者send方法,都要轉交職責,在子生成器上調用next函數,僅當調用方發(fā)送的值不是None時,才使用子生成器的send方法。

完整的偽代碼,參見《流暢的Python》第401頁

使用協程做離散事件仿真

協程能自然地表述很多算法,例如仿真、游戲、異步IO,以及其他事件驅動型變成形式或協作式多任務。--Guido

協程是asyncio包的基礎構。通過仿真系統能夠說明如何使用協程代替線程實現并發(fā)活動。

離散事件仿真(DES:Discrete Event Simulation)是一種把系統建模成一系列事件的仿真類型。

在離散事件仿真中,仿真“鐘”向前推進的量不是固定的,而是直到推進到下一個事件模型的模擬時間。假如我們抽象模擬出租車的運營過程,其中一個事件是乘客上車,下一個事件則是乘客下車。使用離散事件仿真可以在不到一秒鐘的時間內模擬一年的出租車運營過程。這與連續(xù)仿真不同,連續(xù)仿真的仿真鐘以固定的量不斷向前前進。

顯然,回合制游戲就是離散事件仿真的例子:游戲的狀態(tài)只在玩家操作時變化,而且一旦玩家決定下一步怎么走了,仿真鐘就會凍結。而實時游戲則是連續(xù)仿真,仿真鐘一直在運行,游戲的狀態(tài)在一秒鐘內更新很多次。

這兩種仿真類型都能使用多線程或在單線程中使用面向事件的編程技術(例如事件循環(huán)驅動的回調或協程)實現??梢哉f,為了實現連續(xù)仿真,在多個線程中處理實時并行的操作更自然。而協程恰好為實現離散事件仿真提供了合理的抽象。SimPy是一個實現離散事件仿真的Python包,通過一個協程表示離散事件仿真系統的各個進程。

到此這篇關于Python協程原理全面分析的文章就介紹到這了,更多相關Python協程內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Django filter動態(tài)過濾與排序實現過程解析

    Django filter動態(tài)過濾與排序實現過程解析

    這篇文章主要介紹了Django filter動態(tài)過濾與排序實現過程解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-11-11
  • keras.layers.Layer中無法定義name的問題及解決

    keras.layers.Layer中無法定義name的問題及解決

    這篇文章主要介紹了keras.layers.Layer中無法定義name的問題及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-02-02
  • Python django搭建layui提交表單,表格,圖標的實例

    Python django搭建layui提交表單,表格,圖標的實例

    今天小編就為大家分享一篇Python django搭建layui提交表單,表格,圖標的實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2019-11-11
  • python中復數的共軛復數知識點總結

    python中復數的共軛復數知識點總結

    在本篇內容里小編給大家整理的是關于python中復數的共軛復數知識點總結,有需要的朋友們可以學習下。
    2020-12-12
  • help函數解決python所有文檔信息查看

    help函數解決python所有文檔信息查看

    這篇文章主要為大家介紹了help函數解決python所有文檔信息查看示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-06-06
  • 關于python中.xpath的使用問題

    關于python中.xpath的使用問題

    根據xpath定位到了tr,注意瀏覽器自動生成了tbody,在python中要把自動生成的tbody層級去掉,這樣要怎么操作呢?下面通過代碼給大家介紹下python中.xpath的使用問題,感興趣的朋友一起看看吧
    2021-11-11
  • python數據操作之lambda表達式詳情

    python數據操作之lambda表達式詳情

    這篇文章主要介紹了python數據操作之lambda表達式詳情,文章基于python的相關資料展開lambda表達式具體的內容,感興趣的小伙伴可以參考一下
    2022-05-05
  • Python實現將列表拆分為大小為N的塊

    Python實現將列表拆分為大小為N的塊

    這篇文章主要為大家整理了一些常見的Python實現將列表拆分為大小為N的塊的方法,文中的示例代碼講解詳細,具有一定的參考價值,有需要的小伙伴可以了解下
    2023-09-09
  • Django中更新多個對象數據與刪除對象的方法

    Django中更新多個對象數據與刪除對象的方法

    這篇文章主要介紹了Django中更新多個對象數據與刪除對象的方法,Django是Python重多各色框架中人氣最高的一個,需要的朋友可以參考下
    2015-07-07
  • python--shutil移動文件到另一個路徑的操作

    python--shutil移動文件到另一個路徑的操作

    這篇文章主要介紹了python--shutil移動文件到另一個路徑的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-07-07

最新評論