1. 一文搞懂 Python 中的 yield

        共 6051字,需瀏覽 13分鐘

         ·

        2021-04-08 23:04

        ↑↑↑關(guān)注后"星標"簡說Python

        人人都可以簡單入門Python、爬蟲、數(shù)據(jù)分析
         簡說Python推薦 
        作者 |somenzz

        來源 | Python七號

        yield 可以實現(xiàn)生成器,可以實現(xiàn)協(xié)程。什么是生成器,什么是協(xié)程,如果還不了解,可以繼續(xù)往下看,概念可以不懂,只要理解它的作用和效果也是可以的。
        先翻一下英文單詞 yield 的意思:
        to produce or provide sth, for example a profit, result or crop
        中文意思:出產(chǎn)(作物);產(chǎn)生(收益、效益等);提供。

        yield 實現(xiàn)生成器

        初學 Python 之時,我遇到 yield 關(guān)鍵字時,就把它理解為一種特殊的 return,能看懂就行,自己也很少用,也就沒有深入研究過。直到現(xiàn)在,我需要處理大量數(shù)據(jù),數(shù)據(jù)的大小甚至超過了電腦的可用內(nèi)存,此時我想起來 yield。比如,我操作 db2 數(shù)據(jù)庫查詢數(shù)據(jù),當數(shù)據(jù)的結(jié)果很大時,我不想一下子讀入內(nèi)存,我就使用了 yield 關(guān)鍵字返回一行數(shù)據(jù),程序處理完后,再取下一行:
        def read(self, sql, params=()):
        stmt = ibm_db.prepare(self.connection, sql) for index, param in enumerate(params): ibm_db.bind_param(stmt, index + 1, param) ibm_db.execute(stmt) row = ibm_db.fetch_tuple(stmt) while row: yield row row = ibm_db.fetch_tuple(stmt)
        可以這么來理解關(guān)鍵字 yield 的用法:它返回了一個值,但程序并未退出,下一次從 yield 后面的代碼繼續(xù)運行,直到后面沒有代碼,結(jié)束運行。這里我們舉一個簡單的例子看下效果:
        >>> def iter_fun():... print("a")... yield 1... print("b")... yield 2... print("c")... yield 3...>>> iter_fun()<generator object iter_fun at 0x107e372a0>>>> for i in iter_fun():... print(i)...a1b2c3>>> x = iter_fun()>>> x.__next__()a1>>> x.__next__()b2>>> x.__next__()c3>>> x.__next__()Traceback (most recent call last): File "<stdin>", line 1, in <module>StopIteration
        通過上面的例子,我們可以發(fā)現(xiàn),yield 關(guān)鍵子自動為我們生成來私有方法 __next__ ,這樣不會將所有數(shù)據(jù)取出來存入內(nèi)存中,用多少取多少,可以節(jié)省內(nèi)存空間。
        除此之外,yield 在數(shù)據(jù)量較大時,執(zhí)行速度也會提升:
        In [14]: def normal_fun(num): ...: result = [] ...: for i in range(0,num): ...: if i%2 == 0: ...: result.append(i) ...: return result ...:
        In [14]: def iter_fun(num): ...: for i in range(0,num): ...: if i %2 == 0: ...: yield i ...:
        In [15]: %time for i in iter_fun(1000000): a = iCPU times: user 97 ms, sys: 2.55 ms, total: 99.6 msWall time: 97.2 ms
        In [16]: %time for i in normal_fun(1000000): a = iCPU times: user 115 ms, sys: 13.6 ms, total: 129 msWall time: 128 ms
        In [17]: %time for i in normal_fun(100000000): a = iCPU times: user 10.8 s, sys: 668 ms, total: 11.4 sWall time: 11.4 s
        In [18]: %time for i in iter_fun(100000000): a = iCPU times: user 9.1 s, sys: 6.49 ms, total: 9.11 sWall time: 9.12 s
        上述代碼在 Ipython 終端中執(zhí)行,可以看出使用 yield 的函數(shù),執(zhí)行的速度更快。
        yield 是自己實現(xiàn)一個生成器最便捷的方式。而 Python 語言的生成器是最有用的特性之一,也是使用不廣泛的特性,我曾問過周圍用 java 的朋友有沒有類似的特性,答曰沒有,網(wǎng)上搜了下,確實主流的編程語言都沒有,因此 Python 的生成器特性沒有引起其他語言轉(zhuǎn) Python 的工程師的關(guān)注。
        為什么說生成器非常有用呢?
        當你需要處理的數(shù)據(jù)大小超過你電腦的可用內(nèi)存時,生成器的懶加載(用的時才讀入內(nèi)存)就非常有效。
        比如讀文件時,如果你使用下面的方式,文件特別大的話,可能內(nèi)存一下子就滿了:
        with open("file.txt","r") as reader: for line in reader.readlines(): #do something pass
        推薦的做法是使用文件本身的生成器,這樣讀多大的文件,也不會撐爆內(nèi)存:
        with open("file.txt","r") as reader: for line in reader: #do something pass
        其實我們可以看一下 reader 到底有哪些方法:
        >>> with open("/etc/passwd","r") as reader:... dir(reader)...['_CHUNK_SIZE', '__class__', '__del__', '__delattr__', '__dict__', '__dir__', '__doc__', '__enter__', '__eq__', '__exit__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__lt__', '__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '_checkClosed', '_checkReadable', '_checkSeekable', '_checkWritable', '_finalizing', 'buffer', 'close', 'closed', 'detach', 'encoding', 'errors', 'fileno', 'flush', 'isatty', 'line_buffering', 'mode', 'name', 'newlines', 'read', 'readable', 'readline', 'readlines', 'reconfigure', 'seek', 'seekable', 'tell', 'truncate', 'writable', 'write', 'write_through', 'writelines']>>>
        我們可以看到,這里面有一個 __iter__ 方法和一個 __next__ 方法,這兩個方法是生成器的標志。
        想深入學習生成器,迭代器,可迭代對象,可以看我以前的推文:
        python 基礎(chǔ)系列--可迭代對象、迭代器與生成器
        深入理解迭代器和生成器

        yield 可以實現(xiàn)協(xié)程

        yield 關(guān)鍵字還可以實現(xiàn)協(xié)程,雖然現(xiàn)在有來 asyncio 和 await 關(guān)鍵字來方便的實現(xiàn)協(xié)程,在此之前,實現(xiàn)協(xié)程就靠的是 yield。
        yield 有一個 send 方法,可以改變 yield 的返回值,是這樣用的,先看代碼:
        In [20]: def fun(): ...: print("a") ...: a = yield 1 ...: print("b") ...: print("a = ",a) ...: b = yield a ...: print("c") ...: print("b = ",b) ...:
        In [21]: x = fun()
        In [22]: x.__next__()aOut[22]: 1
        In [23]: x.send(4)ba = 4Out[23]: 4
        In [24]: x.send(5)cb = 5---------------------------------------------------------------------------StopIteration Traceback (most recent call last)<ipython-input-24-9183f5e81876> in <module>----> 1 x.send(5)
        第一次執(zhí)行 x 的 __next__ 方法時,函數(shù)執(zhí)行到第一個 yield 處,打印了 a 返回了值 1,此時變量 a 并未獲取到 yield 的返回值,a 為 None ,當執(zhí)行 x.send(4) 時,a 才獲取到值 4,程序運行到第二個 yield 處,后續(xù)過程也是一樣。
        利用這一特性,我們可以和被調(diào)用的函數(shù)通信,進而可以實現(xiàn)一個生產(chǎn)者消費者模型,代碼如下:
        import time
        def consume(): r = '' while True: n = yield r if not n: return print('[consumer] consuming %s...' % n) time.sleep(1) r = 'well received'
        def produce(c): next(c) n = 0 while n < 5: n = n + 1 print('[producer] producing %s...' % n) r = c.send(n) print('[producer] consumer return: %s' % r) c.close()
        if __name__=='__main__': c = consume() produce(c)
        運行結(jié)果如下:
        [producer] producing 1...[consumer] consuming 1...[producer] consumer return: well received[producer] producing 2...[consumer] consuming 2...[producer] consumer return: well received[producer] producing 3...[consumer] consuming 3...[producer] consumer return: well received[producer] producing 4...[consumer] consuming 4...[producer] consumer return: well received[producer] producing 5...[consumer] consuming 5...[producer] consumer return: well received
        produce 和 consume 函數(shù)在一個線程內(nèi)執(zhí)行,通過調(diào)用 send 方法和yield 互相切換,實現(xiàn)協(xié)程的功能。
        -END-
        最后給大家分享《100本Python電子書》,包括Python編程技巧、數(shù)據(jù)分析、爬蟲、Web開發(fā)、機器學習、深度學習。
        現(xiàn)在免費分享出來,有需要的讀者可以下載學習,在下面的公眾號「程序員獅子里回復關(guān)鍵字Python,就行。

        掃下方二維碼添加我的私人微信,可以在我的朋友圈獲取最新的Python學習資料,以及近期推文中的源碼或者其他資源,另外不定期開放學習交流群,以及朋友圈福利(送書、紅包、學習資源等)。

        掃碼查看我朋友圈

        獲取最新學習資源

        學習更多:
        整理了我開始分享學習筆記到現(xiàn)在超過250篇優(yōu)質(zhì)文章,涵蓋數(shù)據(jù)分析、爬蟲、機器學習等方面,別再說不知道該從哪開始,實戰(zhàn)哪里找了

        點贊”傳統(tǒng)美德不能丟 

        瀏覽 41
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
          
          

            1. 国产精品日日躁夜夜躁欧美 | 操你啊啊啊 | 操逼网站在线播放 | 亚洲电影在线观看 | 毛片基地免费看 |