Python Yield 精髓

對(duì) Python 中的 Yield 一直理解的不夠深刻,甚至存在誤解。遇到一個(gè)神奇的用法后(多個(gè) yield 連續(xù)使用)又好好研究了下,以下記錄鄙人粗糙見解。

首先簡(jiǎn)單科普一下 yield:

要理解 yield 語(yǔ)句,關(guān)鍵要理解 python 的生成器。 用官網(wǎng)的說法1、2, 生成器就是一個(gè)返回迭代器(iterator)的函數(shù)。 和普通函數(shù)唯一的區(qū)別就是這個(gè)函數(shù)包含 yield 語(yǔ)句。
包含了 yield 的函數(shù),就是一個(gè)生成器

我是在學(xué)爬蟲 scrapy 框架時(shí)遇見多個(gè)yield 連續(xù)使用
在最后兩行代碼:

# 例1
def parse(self, response):
        # 1取出符合條件的  tr列表
        tr_list = response.xpath('//tr[@class="even"] | //tr[@class="odd"]')

        # 判斷數(shù)據(jù)的有無(wú) ,沒有 到底了
        if not tr_list:
            return

        # 2.遍歷 每一個(gè) tr 取出內(nèi)容
        for tr in tr_list:
            item = TencentItem()
            item['work_name'] = tr.xpath('./td[1]/a/text()').extract_first()
            item['work_type'] = tr.xpath('./td[2]/text()').extract_first()
            item['work_count'] = tr.xpath('./td[3]/text()').extract_first()
            item['work_place'] = tr.xpath('./td[4]/text()').extract_first()
            item['work_time'] = tr.xpath('./td[5]/text()').extract_first()
            item['work_link'] = 'https://hr.tencent.com/' + tr.xpath('./td[1]/a/@href').extract_first()

            # 給 引擎 -->管道
            yield item

            # 告訴 引擎  請(qǐng)求詳情頁(yè)
            yield scrapy.Request(url=item['work_link'], callback=self.detail_parse)

初次看到時(shí),對(duì)于以上代碼執(zhí)行機(jī)制實(shí)在難以理解!
因?yàn)樵谧畛醯睦斫饫铮?yield 有兩個(gè)作用:

  • 暫停執(zhí)行當(dāng)前代碼,并記錄當(dāng)前位置
  • 相當(dāng)于 return, 可以在后面返回值

繼續(xù)執(zhí)行 yield 下面的代碼需要使用 next() 或者 send()。
我就困惑了, 這里兩個(gè) yield 之間沒有使用 next() 或者 send() 方法呀,那么它是怎么執(zhí)行的呢?
然后我自己寫了一個(gè)簡(jiǎn)單的測(cè)試函數(shù),來(lái)探究 yield 的執(zhí)行機(jī)制:

# 例2
# 自定義一個(gè)生成器
def genter():
    a = 4
    b = 5
    c = 6
    for i in range(5):
        yield a
        print('hhh'+str(i))
        yield b
        print("aaa" + str(i))
        yield c

# 包含了yield 的 genter() 就是一個(gè)生成器
res = genter()
for i, c in enumerate(res):
    if i > 1:
        # 通過 break 來(lái)測(cè)試執(zhí)行的結(jié)果
        break
    print(c)

而其結(jié)果是:

4
hhh0
5
aaa0

大家應(yīng)該知道:

在取值時(shí),使用 for 語(yǔ)句,里面封裝了 next 方法。來(lái)一個(gè)個(gè)取出生成器的值,由程序運(yùn)行結(jié)果可知:例2 中 genter() 實(shí)際是通過 多個(gè) yield 實(shí)現(xiàn)了包含多個(gè)值的生成器
顛覆認(rèn)知的是:yield 并沒有暫停,yield 語(yǔ)句后面的 print 正常打印了??!
因此與其說 yield 的作用是暫停并記錄位置, 不如嚴(yán)謹(jǐn)?shù)卣f成:只有在只有一個(gè)yield 的情況下, 才是暫停并記錄位置。函數(shù)的反復(fù)調(diào)用,也是這個(gè)值(迭代器)的反復(fù)調(diào)用;而上述yield 之間的 next 方法就通過 for 循環(huán)調(diào)用了
多個(gè) yield 的情況下,應(yīng)該理解成:這個(gè)函數(shù)本身就是一個(gè)擁有多個(gè)值(迭代器)的迭代器,此時(shí) yield 的暫停, 應(yīng)該暫停于下一個(gè) yield 之前!

一般我們所見到的生成器,只有一個(gè) yield,通過反復(fù)調(diào)用這個(gè)方法,來(lái)實(shí)現(xiàn)所謂的生成器。在這樣的情況下,我們也習(xí)慣地以為 yield 的兩個(gè)作用之一就是暫停執(zhí)行當(dāng)前的代碼, 并記錄當(dāng)前位置,并且有 return 的作用。

不過多個(gè) yield 和單個(gè) yield 的情況,為何會(huì)不一樣呢?
其底層的原理應(yīng)該一樣才對(duì)!

對(duì)于這個(gè),啃了下官方文檔,沒看太明白。可能要去學(xué)了 C 語(yǔ)言才能理解?,F(xiàn)在粗淺理解:之所以包含了 yield 關(guān)鍵字的函數(shù)就是一個(gè)生成器, 是因?yàn)?yield自身就是生成器!

那么對(duì)于例1 中的情況就很好理解了,parse 本身是一個(gè)生成器,scrapy 引擎會(huì)在調(diào)用其中值時(shí)添加next() 方法吧~
另外,除了官方文檔, 還可以看下這篇講協(xié)程的文檔。

由于學(xué)藝未精,如有錯(cuò)誤,望不吝賜教,謝謝!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 你不知道JS:異步 第四章:生成器(Generators) 在第二章,我們明確了采用回調(diào)表示異步流的兩個(gè)關(guān)鍵缺點(diǎn):...
    purple_force閱讀 1,044評(píng)論 0 2
  • 你不知道JS:異步 第四章:生成器(Generators) 接上篇4-1 生成器委托(Generator Dele...
    purple_force閱讀 596評(píng)論 0 1
  • 簡(jiǎn)介 基本概念 Generator函數(shù)是ES6提供的一種異步編程解決方案,語(yǔ)法行為與傳統(tǒng)函數(shù)完全不同。本章詳細(xì)介紹...
    呼呼哥閱讀 1,136評(píng)論 0 4
  • 本文作者就是我,簡(jiǎn)書的microkof。如果您覺得本文對(duì)您的工作有意義,產(chǎn)生了不可估量的價(jià)值,那么請(qǐng)您不吝打賞我,...
    microkof閱讀 23,852評(píng)論 16 78
  • 讀方浚頤(1815-1888)《二知軒文存》,三十四卷,光緒四年刻本。 1.守變——二知軒文存,卷一,第35頁(yè)。 ...
    淺智閱讀 1,277評(píng)論 2 0

友情鏈接更多精彩內(nèi)容