国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

python協(xié)程1:yield 10分鐘入門

MartinDai / 3210人閱讀

摘要:協(xié)程定義協(xié)程的底層架構(gòu)是在中定義,并在實(shí)現(xiàn)的。為了簡化,我們會(huì)使用裝飾器預(yù)激協(xié)程。執(zhí)行上述代碼結(jié)果如下出錯(cuò)的原因是發(fā)送給協(xié)程的值不能加到變量上。示例使用和方法控制協(xié)程。

最近找到一本python好書《流暢的python》,是到現(xiàn)在為止看到的對python高級(jí)特性講述最詳細(xì)的一本。
看了協(xié)程一章,做個(gè)讀書筆記,加深印象。

協(xié)程定義

協(xié)程的底層架構(gòu)是在pep342 中定義,并在python2.5 實(shí)現(xiàn)的。

python2.5 中,yield關(guān)鍵字可以在表達(dá)式中使用,而且生成器API中增加了 .send(value)方法。生成器可以使用.send(...)方法發(fā)送數(shù)據(jù),發(fā)送的數(shù)據(jù)會(huì)成為生成器函數(shù)中yield表達(dá)式的值。

協(xié)程是指一個(gè)過程,這個(gè)過程與調(diào)用方協(xié)作,產(chǎn)出有調(diào)用方提供的值。因此,生成器可以作為協(xié)程使用。

除了 .send(...)方法,pep342 和添加了 .throw(...)(讓調(diào)用方拋出異常,在生成器中處理)和.close()(終止生成器)方法。

python3.3后,pep380對生成器函數(shù)做了兩處改動(dòng):

生成器可以返回一個(gè)值;以前,如果生成器中給return語句提供值,會(huì)拋出SyntaxError異常。

引入yield from 語法,使用它可以把復(fù)雜的生成器重構(gòu)成小型的嵌套生成器,省去之前把生成器的工作委托給子生成器所需的大量模板代碼。

協(xié)程生成器的基本行為

首先說明一下,協(xié)程有四個(gè)狀態(tài),可以使用inspect.getgeneratorstate(...)函數(shù)確定:

GEN_CREATED # 等待開始執(zhí)行

GEN_RUNNING # 解釋器正在執(zhí)行(只有在多線程應(yīng)用中才能看到這個(gè)狀態(tài))

GEN_SUSPENDED # 在yield表達(dá)式處暫停

GEN_CLOSED # 執(zhí)行結(jié)束

#! -*- coding: utf-8 -*-
import inspect

# 協(xié)程使用生成器函數(shù)定義:定義體中有yield關(guān)鍵字。
def simple_coroutine():
    print("-> coroutine started")
    # yield 在表達(dá)式中使用;如果協(xié)程只需要從客戶那里接收數(shù)據(jù),yield關(guān)鍵字右邊不需要加表達(dá)式(yield默認(rèn)返回None)
    x = yield
    print("-> coroutine received:", x)


my_coro = simple_coroutine()
my_coro # 和創(chuàng)建生成器的方式一樣,調(diào)用函數(shù)得到生成器對象。
# 協(xié)程處于 GEN_CREATED (等待開始狀態(tài))
print(inspect.getgeneratorstate(my_coro))

my_coro.send(None)
# 首先要調(diào)用next()函數(shù),因?yàn)樯善鬟€沒有啟動(dòng),沒有在yield語句處暫停,所以開始無法發(fā)送數(shù)據(jù)
# 發(fā)送 None 可以達(dá)到相同的效果 my_coro.send(None) 
next(my_coro)
# 此時(shí)協(xié)程處于 GEN_SUSPENDED (在yield表達(dá)式處暫停)
print(inspect.getgeneratorstate(my_coro))

# 調(diào)用這個(gè)方法后,協(xié)程定義體中的yield表達(dá)式會(huì)計(jì)算出42;現(xiàn)在協(xié)程會(huì)恢復(fù),一直運(yùn)行到下一個(gè)yield表達(dá)式,或者終止。
my_coro.send(42)
print(inspect.getgeneratorstate(my_coro))

運(yùn)行上述代碼,輸出結(jié)果如下

GEN_CREATED
-> coroutine started
GEN_SUSPENDED
-> coroutine received: 42

# 這里,控制權(quán)流動(dòng)到協(xié)程定義體的尾部,導(dǎo)致生成器像往常一樣拋出StopIteration異常
Traceback (most recent call last):
  File "/Users/gs/coroutine.py", line 18, in  
    my_coro.send(42)
StopIteration

send方法的參數(shù)會(huì)成為暫停yield表達(dá)式的值,所以,僅當(dāng)協(xié)程處于暫停狀態(tài)是才能調(diào)用send方法。
如果協(xié)程還未激活(GEN_CREATED 狀態(tài))要調(diào)用next(my_coro) 激活協(xié)程,也可以調(diào)用my_coro.send(None)

如果創(chuàng)建協(xié)程對象后立即把None之外的值發(fā)給它,會(huì)出現(xiàn)下述錯(cuò)誤:

>>> my_coro = simple_coroutine()
>>> my_coro.send(123)

Traceback (most recent call last):
  File "/Users/gs/coroutine.py", line 14, in 
    my_coro.send(123)
TypeError: can"t send non-None value to a just-started generator

仔細(xì)看錯(cuò)誤消息

can"t send non-None value to a just-started generator

最先調(diào)用next(my_coro) 這一步通常稱為”預(yù)激“(prime)協(xié)程---即,讓協(xié)程向前執(zhí)行到第一個(gè)yield表達(dá)式,準(zhǔn)備好作為活躍的協(xié)程使用。

再看一個(gè)兩個(gè)值得協(xié)程
def simple_coro2(a):
    print("-> coroutine started: a =", a)
    b = yield a
    print("-> Received: b =", b)
    c = yield a + b
    print("-> Received: c =", c)

my_coro2 = simple_coro2(14)
print(inspect.getgeneratorstate(my_coro2))
# 這里inspect.getgeneratorstate(my_coro2) 得到結(jié)果為 GEN_CREATED (協(xié)程未啟動(dòng))

next(my_coro2)
# 向前執(zhí)行到第一個(gè)yield 處 打印 “-> coroutine started: a = 14”
# 并且產(chǎn)生值 14 (yield a 執(zhí)行 等待為b賦值)
print(inspect.getgeneratorstate(my_coro2))
# 這里inspect.getgeneratorstate(my_coro2) 得到結(jié)果為 GEN_SUSPENDED (協(xié)程處于暫停狀態(tài))

my_coro2.send(28)
# 向前執(zhí)行到第二個(gè)yield 處 打印 “-> Received: b = 28”
# 并且產(chǎn)生值 a + b = 42(yield a + b 執(zhí)行 得到結(jié)果42 等待為c賦值)
print(inspect.getgeneratorstate(my_coro2))
# 這里inspect.getgeneratorstate(my_coro2) 得到結(jié)果為 GEN_SUSPENDED (協(xié)程處于暫停狀態(tài))

my_coro2.send(99)
# 把數(shù)字99發(fā)送給暫停協(xié)程,計(jì)算yield 表達(dá)式,得到99,然后把那個(gè)數(shù)賦值給c 打印 “-> Received: c = 99”
# 協(xié)程終止,拋出StopIteration

運(yùn)行上述代碼,輸出結(jié)果如下

GEN_CREATED
-> coroutine started: a = 14
GEN_SUSPENDED
-> Received: b = 28
-> Received: c = 99

Traceback (most recent call last):
  File "/Users/gs/coroutine.py", line 37, in 
    my_coro2.send(99)
StopIteration

simple_coro2 協(xié)程的執(zhí)行過程分為3個(gè)階段,如下圖所示

調(diào)用next(my_coro2),打印第一個(gè)消息,然后執(zhí)行yield a,產(chǎn)出數(shù)字14.

調(diào)用my_coro2.send(28),把28賦值給b,打印第二個(gè)消息,然后執(zhí)行 yield a + b 產(chǎn)生數(shù)字42

調(diào)用my_coro2.send(99),把99賦值給c,然后打印第三個(gè)消息,協(xié)程終止。

使用裝飾器預(yù)激協(xié)程

我們已經(jīng)知道,協(xié)程如果不預(yù)激,不能使用send() 傳入非None 數(shù)據(jù)。所以,調(diào)用my_coro.send(x)之前,一定要調(diào)用next(my_coro)。
為了簡化,我們會(huì)使用裝飾器預(yù)激協(xié)程。

from functools import wraps

def coroutinue(func):
    """
    裝飾器: 向前執(zhí)行到第一個(gè)`yield`表達(dá)式,預(yù)激`func`
    :param func: func name
    :return: primer
    """

    @wraps(func)
    def primer(*args, **kwargs):
        # 把裝飾器生成器函數(shù)替換成這里的primer函數(shù);調(diào)用primer函數(shù)時(shí),返回預(yù)激后的生成器。
        gen = func(*args, **kwargs)
        # 調(diào)用被被裝飾函數(shù),獲取生成器對象
        next(gen)  # 預(yù)激生成器
        return gen  # 返回生成器
    return primer


# 使用方法如下

@coroutinue
def simple_coro(a):
    a = yield

simple_coro(12)  # 已經(jīng)預(yù)激
終止協(xié)程和異常處理

協(xié)程中,為處理的異常會(huì)向上冒泡,傳遞給next函數(shù)或send方法的調(diào)用方,未處理的異常會(huì)導(dǎo)致協(xié)程終止。

看下邊這個(gè)例子

#! -*- coding: utf-8 -*-

from functools import wraps

def coroutinue(func):
    """
    裝飾器: 向前執(zhí)行到第一個(gè)`yield`表達(dá)式,預(yù)激`func`
    :param func: func name
    :return: primer
    """

    @wraps(func)
    def primer(*args, **kwargs):
        # 把裝飾器生成器函數(shù)替換成這里的primer函數(shù);調(diào)用primer函數(shù)時(shí),返回預(yù)激后的生成器。
        gen = func(*args, **kwargs)
        # 調(diào)用被被裝飾函數(shù),獲取生成器對象
        next(gen)  # 預(yù)激生成器
        return gen  # 返回生成器
    return primer


@coroutinue
def averager():
    # 使用協(xié)程求平均值
    total = 0.0
    count = 0
    average = None
    while True:
        term = yield average
        total += term
        count += 1
        average = total/count

coro_avg = averager()
print(coro_avg.send(40))
print(coro_avg.send(50))
print(coro_avg.send("123")) # 由于發(fā)送的不是數(shù)字,導(dǎo)致內(nèi)部有異常拋出。

執(zhí)行上述代碼結(jié)果如下

40.0
45.0
Traceback (most recent call last):
  File "/Users/gs/coro_exception.py", line 37, in 
    print(coro_avg.send("123"))
  File "/Users/gs/coro_exception.py", line 30, in averager
    total += term
TypeError: unsupported operand type(s) for +=: "float" and "str"

出錯(cuò)的原因是發(fā)送給協(xié)程的"123"值不能加到total變量上。
出錯(cuò)后,如果再次調(diào)用 coro_avg.send(x) 方法 會(huì)拋出 StopIteration 異常。

由上邊的例子我們可以知道,如果想讓協(xié)程退出,可以發(fā)送給它一個(gè)特定的值。比如None和Ellipsis。(推薦使用Ellipsis,因?yàn)槲覀儾惶褂眠@個(gè)值)
從Python2.5 開始,我們可以在生成器上調(diào)用兩個(gè)方法,顯式的把異常發(fā)給協(xié)程。
這兩個(gè)方法是throw和close。

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

這個(gè)方法使生成器在暫停的yield表達(dá)式處拋出指定的異常。如果生成器處理了拋出的異常,代碼會(huì)向前執(zhí)行到下一個(gè)yield表達(dá)式,而產(chǎn)出的值會(huì)成為調(diào)用throw方法得到的返回值。如果沒有處理,則向上冒泡,直接拋出。

generator.close()

生成器在暫停的yield表達(dá)式處拋出GeneratorExit異常。
如果生成器沒有處理這個(gè)異?;蛘邟伋隽薙topIteration異常,調(diào)用方不會(huì)報(bào)錯(cuò)。如果收到GeneratorExit異常,生成器一定不能產(chǎn)出值,否則解釋器會(huì)拋出RuntimeError異常。

示例: 使用close和throw方法控制協(xié)程。
import inspect


class DemoException(Exception):
    pass


@coroutinue
def exc_handling():
    print("-> coroutine started")
    while True:
        try:
            x = yield
        except DemoException:
            print("*** DemoException handled. Conginuing...")
        else:
            # 如果沒有異常顯示接收到的值
            print("--> coroutine received: {!r}".format(x))
    raise RuntimeError("This line should never run.")  # 這一行永遠(yuǎn)不會(huì)執(zhí)行 


exc_coro = exc_handling()

exc_coro.send(11)
exc_coro.send(12)
exc_coro.send(13)
exc_coro.close()
print(inspect.getgeneratorstate(exc_coro))

raise RuntimeError("This line should never run.") 永遠(yuǎn)不會(huì)執(zhí)行,因?yàn)橹挥形刺幚淼漠惓2艜?huì)終止循環(huán),而一旦出現(xiàn)未處理的異常,協(xié)程會(huì)立即終止。

執(zhí)行上述代碼得到結(jié)果為:

-> coroutine started
--> coroutine received: 11
--> coroutine received: 12
--> coroutine received: 13
GEN_CLOSED    # 協(xié)程終止

上述代碼,如果傳入DemoException,協(xié)程不會(huì)中止,因?yàn)樽隽水惓L幚怼?/p>

exc_coro = exc_handling()

exc_coro.send(11)
exc_coro.send(12)
exc_coro.send(13)
exc_coro.throw(DemoException) # 協(xié)程不會(huì)中止,但是如果傳入的是未處理的異常,協(xié)程會(huì)終止
print(inspect.getgeneratorstate(exc_coro))
exc_coro.close()
print(inspect.getgeneratorstate(exc_coro))

## output

-> coroutine started
--> coroutine received: 11
--> coroutine received: 12
--> coroutine received: 13
*** DemoException handled. Conginuing...
GEN_SUSPENDED
GEN_CLOSED

如果不管協(xié)程如何結(jié)束都想做些處理工作,要把協(xié)程定義體重的相關(guān)代碼放入try/finally塊中。

@coroutinue
def exc_handling():
    print("-> coroutine started")
    try:
        while True:
            try:
                x = yield
            except DemoException:
                print("*** DemoException handled. Conginuing...")
            else:
                # 如果沒有異常顯示接收到的值
                print("--> coroutine received: {!r}".format(x))
    finally:
        print("-> coroutine ending")

上述部分介紹了:

生成器作為協(xié)程使用時(shí)的行為和狀態(tài)

使用裝飾器預(yù)激協(xié)程

調(diào)用方如何使用生成器對象的 .throw(...)和.close() 方法控制協(xié)程

下一部分將介紹:

協(xié)程終止時(shí)如何返回值

yield新句法的用途和語義

最后,感謝女朋友支持。

>歡迎關(guān)注 >請我喝芬達(dá)

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/44439.html

相關(guān)文章

  • python協(xié)程3:用仿真實(shí)驗(yàn)學(xué)習(xí)協(xié)程

    摘要:徘徊和行程所用的時(shí)間使用指數(shù)分布生成,我們將時(shí)間設(shè)為分鐘數(shù),以便顯示清楚。迭代表示各輛出租車的進(jìn)程在各輛出租車上調(diào)用函數(shù),預(yù)激協(xié)程。 前兩篇我們已經(jīng)介紹了python 協(xié)程的使用和yield from 的原理,這一篇,我們用一個(gè)例子來揭示如何使用協(xié)程在單線程中管理并發(fā)活動(dòng)。。 什么是離散事件仿真 Wiki上的定義是: 離散事件仿真將系統(tǒng)隨時(shí)間的變化抽象成一系列的離散時(shí)間點(diǎn)上的事件,通過...

    banana_pi 評(píng)論0 收藏0
  • python協(xié)程2:yield from 從入門到精通

    摘要:于此同時(shí),會(huì)阻塞,等待終止。子生成器返回之后,解釋器會(huì)拋出異常,并把返回值附加到異常對象上,只是委派生成器恢復(fù)。實(shí)例運(yùn)行完畢后,返回的值綁定到上。這一部分處理調(diào)用方通過方法傳入的異常。之外的異常會(huì)向上冒泡。 上一篇python協(xié)程1:yield的使用介紹了: 生成器作為協(xié)程使用時(shí)的行為和狀態(tài) 使用裝飾器預(yù)激協(xié)程 調(diào)用方如何使用生成器對象的 .throw(...) 和 .close()...

    vpants 評(píng)論0 收藏0
  • Python中的協(xié)程

    摘要:協(xié)程的基本行為協(xié)程包含四種狀態(tài)等待開始執(zhí)行。協(xié)程中重要的兩個(gè)方法調(diào)用方把數(shù)據(jù)提供給協(xié)程。注意使用調(diào)用協(xié)程時(shí)會(huì)自動(dòng)預(yù)激,因此與裝飾器不兼容標(biāo)準(zhǔn)庫中的裝飾器不會(huì)預(yù)激協(xié)程,因此能兼容句法。因此,終止協(xié)程的本質(zhì)在于向協(xié)程發(fā)送其無法處理的異常。 導(dǎo)語:本文章記錄了本人在學(xué)習(xí)Python基礎(chǔ)之控制流程篇的重點(diǎn)知識(shí)及個(gè)人心得,打算入門Python的朋友們可以來一起學(xué)習(xí)并交流。 本文重點(diǎn): 1、掌握協(xié)...

    shinezejian 評(píng)論0 收藏0
  • Python協(xié)程(真才實(shí)學(xué),想學(xué)的進(jìn)來)

    摘要:所以與多線程相比,線程的數(shù)量越多,協(xié)程性能的優(yōu)勢越明顯。值得一提的是,在此過程中,只有一個(gè)線程在執(zhí)行,因此這與多線程的概念是不一樣的。 真正有知識(shí)的人的成長過程,就像麥穗的成長過程:麥穗空的時(shí)候,麥子長得很快,麥穗驕傲地高高昂起,但是,麥穗成熟飽滿時(shí),它們開始謙虛,垂下麥芒。 ——蒙田《蒙田隨筆全集》 上篇論述了關(guān)于python多線程是否是雞肋的問題,得到了一些網(wǎng)友的認(rèn)可,當(dāng)然也有...

    lykops 評(píng)論0 收藏0
  • Python中的并發(fā)處理之使用asyncio包

    摘要:并發(fā)用于制定方案,用來解決可能但未必并行的問題。在協(xié)程中使用需要注意兩點(diǎn)使用鏈接的多個(gè)協(xié)程最終必須由不是協(xié)程的調(diào)用方驅(qū)動(dòng),調(diào)用方顯式或隱式在最外層委派生成器上調(diào)用函數(shù)或方法。對象可以取消取消后會(huì)在協(xié)程當(dāng)前暫停的處拋出異常。 導(dǎo)語:本文章記錄了本人在學(xué)習(xí)Python基礎(chǔ)之控制流程篇的重點(diǎn)知識(shí)及個(gè)人心得,打算入門Python的朋友們可以來一起學(xué)習(xí)并交流。 本文重點(diǎn): 1、了解asyncio...

    tuniutech 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<