<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          深度好文:Python之列表生成式、生成器、可迭代對(duì)象與迭代器(二)

          共 3811字,需瀏覽 8分鐘

           ·

          2019-11-27 23:23

          b54c0cb227be78534e89a18fcea8e9d7.webp

          作者:云游道士

          原文:https://www.cnblogs.com/yyds/p/6281453.html


          三、生成器(Generator)



          從名字上來看,生成器應(yīng)該是用來生成數(shù)據(jù)的。


          1. 生成器的作用


          按照某種算法不斷生成新的數(shù)據(jù),直到滿足某一個(gè)指定的條件結(jié)束。


          2. 生成器的構(gòu)造方式


          構(gòu)造生成器的兩種方式:


          • 使用類似列表生成式的方式生成 (2*n + 1 for n in range(3, 11))

          • 使用包含yield的函數(shù)來生成


          如果計(jì)算過程比較簡(jiǎn)單,可以直接把列表生成式改成generator;但是,如果計(jì)算過程比較復(fù)雜,就只能通過包含yield的函數(shù)來構(gòu)造generator。


          說明:Python 3.3之前的版本中,不允許迭代函數(shù)法中包含return語句。


          3. 生成器構(gòu)造實(shí)例

          # 使用類似列表生成式的方式構(gòu)造生成器g1 = (2*n + 1 for n in range(3, 6))
          # 使用包含yield的函數(shù)構(gòu)造生成器def my_range(start, end): for n in range(start, end): yield 2*n + 1
          g2 = my_range(3, 6)print(type(g1))print(type(g2))

          輸出結(jié)果:

          <class 'generator'><class 'generator'>

          4. 生成器的執(zhí)行過程與特性


          生成器的執(zhí)行過程:


          在執(zhí)行過程中,遇到y(tǒng)ield關(guān)鍵字就會(huì)中斷執(zhí)行,下次調(diào)用則繼續(xù)從上次中斷的位置繼續(xù)執(zhí)行。


          生成器的特性:

          • 只有在調(diào)用時(shí)才會(huì)生成相應(yīng)的數(shù)據(jù)

          • 只記錄當(dāng)前的位置

          • 只能next,不能prev


          5. 生成器的調(diào)用方式


          要調(diào)用生成器產(chǎn)生新的元素,有兩種方式:

          • 調(diào)用內(nèi)置的next()方法

          • 使用循環(huán)對(duì)生成器對(duì)象進(jìn)行遍歷(推薦)

          • 調(diào)用生成器對(duì)象的send()方法


          實(shí)例1:使用next()方法遍歷生成器

          print(next(g1))print(next(g1))print(next(g1))print(next(g1))

          輸出結(jié)果:

          7911Traceback (most recent call last):  File "***/generator.py", line 26, in <module>    print(next(g1))StopIteration


          print(next(g2))print(next(g2))print(next(g2))print(next(g2))

          輸出結(jié)果:

          7911Traceback (most recent call last):  File "***/generator.py", line 31, in <module>    print(next(g2))StopIteration

          可見,使用next()方法遍歷生成器時(shí),最后是以拋出一個(gè)StopIeration異常終止。


          實(shí)例2:使用循環(huán)遍歷生成器

          for x in g1:    print(x)
          for x in g2: print(x)


          兩個(gè)循環(huán)的輸出結(jié)果是一樣的:

          7911


          可見,使用循環(huán)遍歷生成器時(shí)比較簡(jiǎn)潔,且最后不會(huì)拋出一個(gè)StopIeration異常。因此使用循環(huán)的方式遍歷生成器的方式才是被推薦的。

          需要說明的是:如果生成器函數(shù)有返回值,要獲取該返回值的話,只能通過在一個(gè)while循環(huán)中不斷的next(),最后通過捕獲StopIteration異常

          實(shí)例3:調(diào)用生成器對(duì)象的send()方法

          def my_range(start, end):    for n in range(start, end):        ret = yield 2*n + 1        print(ret)
          g3 = my_range(3, 6)
          print(g3.send(None))print(g3.send('hello01'))print(g3.send('hello02'))

          輸出結(jié)果:

          7hello019hello0211
          print(next(g3))print(next(g3))print(next(g3))

          輸出結(jié)果:

          7None9None11


          結(jié)論:

          • next()會(huì)調(diào)用yield,但不給它傳值

          • send()會(huì)調(diào)用yield,也會(huì)給它傳值(該值將成為當(dāng)前yield表達(dá)式的結(jié)果值)

          需要注意的是:第一次調(diào)用生成器的send()方法時(shí),參數(shù)只能為None,否則會(huì)拋出異常。當(dāng)然也可以在調(diào)用send()方法之前先調(diào)用一次next()方法,目的是讓生成器先進(jìn)入yield表達(dá)式。

          6. 生成器與列表生成式對(duì)比


          既然通過列表生成式就可以直接創(chuàng)建一個(gè)新的list,那么為什么還要有生成器存在呢?

          因?yàn)榱斜砩墒绞侵苯觿?chuàng)建一個(gè)新的list,它會(huì)一次性地把所有數(shù)據(jù)都存放到內(nèi)存中,這會(huì)存在以下幾個(gè)問題:


          • 內(nèi)存容量有限,因此列表容量是有限的;

          • 當(dāng)列表中的數(shù)據(jù)量很大時(shí),會(huì)占用大量的內(nèi)存空間,如果我們僅僅需要訪問前面有限個(gè)元素時(shí),就會(huì)造成內(nèi)存資源的極大浪費(fèi);

          • 當(dāng)數(shù)據(jù)量很大時(shí),列表生成式的返回時(shí)間會(huì)很慢;


          而生成器中的元素是按照指定的算法推算出來的,只有調(diào)用時(shí)才生成相應(yīng)的數(shù)據(jù)。這樣就不必一次性地把所有數(shù)據(jù)都生成,從而節(jié)省了大量的內(nèi)存空間,這使得其生成的元素個(gè)數(shù)幾乎是沒有限制的,并且操作的返回時(shí)間也是非常快速的(僅僅是創(chuàng)建一個(gè)變量而已)。


          我們可以做個(gè)試驗(yàn):對(duì)比一下生成一個(gè)1000萬個(gè)數(shù)字的列表,分別看下用列表生成式和生成器時(shí)返回結(jié)果的時(shí)間和所占內(nèi)存空間的大小:

          import timeimport sys
          time_start = time.time()g1 = [x for x in range(10000000)]time_end = time.time()print('列表生成式返回結(jié)果花費(fèi)的時(shí)間:%s' % (time_end - time_start))print('列表生成式返回結(jié)果占用內(nèi)存大小:%s' % sys.getsizeof(g1))
          def my_range(start, end): for x in range(start, end): yield x
          time_start = time.time()g2 = my_range(0, 10000000)time_end = time.time()print('生成器返回結(jié)果花費(fèi)的時(shí)間:%s' % (time_end - time_start))print('生成器返回結(jié)果占用內(nèi)存大小:%s' % sys.getsizeof(g2))


          輸出結(jié)果:


          列表生成式返回結(jié)果花費(fèi)的時(shí)間:0.8215489387512207
          列表生成式返回結(jié)果占用內(nèi)存大小:81528056
          生成器返回結(jié)果花費(fèi)的時(shí)間:0.0

          生成器返回結(jié)果占用內(nèi)存大小:88


          可見,生成器返回結(jié)果的時(shí)間幾乎為0,結(jié)果所占內(nèi)存空間的大小相對(duì)于列表生成器來說也要小的多。


          四、可迭代對(duì)象(Iterable)


          我們經(jīng)常在Python的文檔中看到“Iterable”這個(gè)此,它的意思是“可迭代對(duì)象”。那么什么是可迭代對(duì)象呢?


          可直接用于for循環(huán)的對(duì)象統(tǒng)稱為可迭代對(duì)象(Iterable)。


          目前我們已經(jīng)知道的可迭代(可用于for循環(huán))的數(shù)據(jù)類型有:


          • 集合數(shù)據(jù)類型:如list、tuple、dict、set、str等

          • 生成器(Generator)


          可以使用isinstance()來判斷一個(gè)對(duì)象是否是Iterable對(duì)象:

          from collections import Iterableprint(isinstance([], Iterable))

          五、迭代器(Iterator)




          1. 迭代器的定義

          可以被next()函數(shù)調(diào)用并不斷返回下一個(gè)值的對(duì)象稱為迭代器:Iterator。

          很明顯上面講的生成器也是迭代器。當(dāng)然,我們可以使用isinstance()來驗(yàn)證一下:

          from collections import Iteratorprint(isinstance((x for x in range(5)), Iterator))


          輸出結(jié)果為:True


          2. 對(duì)迭代器的理解


          實(shí)際上,Python中的Iterator對(duì)象表示的是一個(gè)數(shù)據(jù)流,Iterator可以被next()函數(shù)調(diào)用被不斷返回下一個(gè)數(shù)據(jù),直到?jīng)]有數(shù)據(jù)可以返回時(shí)拋出StopIteration異常錯(cuò)誤。可以把這個(gè)數(shù)據(jù)流看做一個(gè)有序序列,但我們無法提前知道這個(gè)序列的長(zhǎng)度。同時(shí),Iterator的計(jì)算是惰性的,只有通過next()函數(shù)時(shí)才會(huì)計(jì)算并返回下一個(gè)數(shù)據(jù)。

          生成器也是這樣的,因?yàn)樯善饕彩堑鳌?/span>


          六、Iterable、Iterator與Generator之間的關(guān)系



          • 生成器對(duì)象既是可迭代對(duì)象,也是迭代器:?我們已經(jīng)知道,生成器不但可以作用與for循環(huán),還可以被next()函數(shù)不斷調(diào)用并返回下一個(gè)值,直到最后拋出StopIteration錯(cuò)誤表示無法繼續(xù)返回下一個(gè)值了。也就是說,生成器同時(shí)滿足可迭代對(duì)象和迭代器的定義;

          • 迭代器對(duì)象一定是可迭代對(duì)象,反之則不一定:?例如list、dict、str等集合數(shù)據(jù)類型是可迭代對(duì)象,但不是迭代器,但是它們可以通過iter()函數(shù)生成一個(gè)迭代器對(duì)象。


          也就是說:迭代器、生成器和可迭代對(duì)象都可以用for循環(huán)去迭代,生成器和迭代器還可以被next()方函數(shù)調(diào)用并返回下一個(gè)值。


          覺得不錯(cuò),點(diǎn)個(gè)在看唄!

          瀏覽 43
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  欧美性爱手机版 | 青娱乐免费在线 | 中文字幕第十二页 | 奇米四色777 | 国产乱伦高清无码 |