乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      python迭代器與生成器小結(jié) | 天空的城

       imelee 2017-05-26

      2016.3.10關(guān)于例子解釋的補(bǔ)充更新

      例子

      老規(guī)矩,先上一個(gè)代碼:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      def add(s, x):
      return s + x
      def gen():
      for i in range(4):
      yield i
      base = gen()
      for n in [1, 10]:
      base = (add(i, n) for i in base)
      print list(base)

      這個(gè)東西輸出可以腦補(bǔ)一下, 結(jié)果是[20,21,22,23], 而不是[10, 11, 12, 13]。 當(dāng)時(shí)糾結(jié)了半天,一直沒(méi)搞懂,后來(lái)齊老師稍微指點(diǎn)了一下, 突然想明白了–真夠笨的,唉。。好了–正好趁機(jī)會(huì)稍微小結(jié)一下python里面的生成器。


      迭代器(iterator)

      要說(shuō)生成器,必須首先說(shuō)迭代器;

      iterable,iterator與itertion

      講到迭代器,就需要區(qū)別幾個(gè)概念:iterable,iterator,itertion, 看著都差不多,其實(shí)不然。下面區(qū)分一下。

      • itertion: 就是迭代,一個(gè)接一個(gè)(one after another),是一個(gè)通用的概念,比如一個(gè)循環(huán)遍歷某個(gè)數(shù)組。
      • iterable: 這個(gè)是可迭代對(duì)象,屬于python的名詞,范圍也很廣,可重復(fù)迭代,滿足如下其中之一的都是iterable:
        • 可以for循環(huán): for i in iterable
        • 可以按index索引的對(duì)象,也就是定義了__getitem__方法,比如list,str;
        • 定義了__iter__方法??梢噪S意返回。
        • 可以調(diào)用iter(obj)的對(duì)象,并且返回一個(gè)iterator
      • iterator: 迭代器對(duì)象,也屬于python的名詞,只能迭代一次。需要滿足如下的迭代器協(xié)議
        • 定義了__iter__方法,但是必須返回自身
        • 定義了next方法,在python3.x是__next__用來(lái)返回下一個(gè)值,并且當(dāng)沒(méi)有數(shù)據(jù)了,拋出StopIteration
        • 可以保持當(dāng)前的狀態(tài)

      首先str和listiterable 但不是iterator:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      In [3]: s = 'hi'
      In [4]: s.__getitem__
      Out[4]: <method-wrapper '__getitem__' of str object at 0x7f9457eed580>
      In [5]: s.next # 沒(méi)有next方法
      ---------------------------------------------------------------------------
      AttributeError Traceback (most recent call last)
      <ipython-input-5-136d3c11be25> in <module>()
      ----> 1 s.next
      AttributeError: 'str' object has no attribute 'next'
      In [6]: l = [1,2] # 同理
      In [7]: l.__iter__
      Out[7]: <method-wrapper '__iter__' of list object at 0x7f945328c320>
      In [8]: l.next
      ---------------------------------------------------------------------------
      AttributeError Traceback (most recent call last)
      <ipython-input-8-c6f8fb94c4cd> in <module>()
      ----> 1 l.next
      AttributeError: 'list' object has no attribute 'next'
      In [9]: iter(s) is s #iter() 沒(méi)有返回本身
      Out[9]: False
      In [10]: iter(l) is l #同理
      Out[10]: False

      但是對(duì)于iterator則不一樣如下, 另外iterable可以支持多次迭代,而iterator在多次next之后,再次調(diào)用就會(huì)拋異常,只可以迭代一次。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      In [13]: si = iter(s)
      In [14]: si
      Out[14]: <iterator at 0x7f9453279dd0>
      In [15]: si.__iter__ # 有__iter__
      Out[15]: <method-wrapper '__iter__' of iterator object at 0x7f9453279dd0>
      In [16]: si.next #擁有next
      Out[16]: <method-wrapper 'next' of iterator object at 0x7f9453279dd0>
      In [20]: si.__iter__() is si #__iter__返回自己
      Out[20]: True

      這樣,由這幾個(gè)例子可以解釋清楚這幾個(gè)概念的區(qū)別。

      自定義iterator 與數(shù)據(jù)分離

      說(shuō)到這里,迭代器對(duì)象基本出來(lái)了。下面大致說(shuō)一下,如何讓自定義的類的對(duì)象成為迭代器對(duì)象,其實(shí)就是定義__iter__next方法:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      In [1]: %paste
      class DataIter(object):
      def __init__(self, *args):
      self.data = list(args)
      self.ind = 0
      def __iter__(self): #返回自身
      return self
      def next(self): # 返回?cái)?shù)據(jù)
      if self.ind == len(self.data):
      raise StopIteration
      else:
      data = self.data[self.ind]
      self.ind += 1
      return data
      ## -- End pasted text --
      In [9]: d = DataIter(1,2)
      In [10]: for x in d: # 開(kāi)始迭代
      ....: print x
      ....:
      1
      2
      In [13]: d.next() # 只能迭代一次,再次使用則會(huì)拋異常
      ---------------------------------------------------------------------------
      StopIteration Traceback (most recent call last)
      ----> 1 d.next()
      <ipython-input-1-c44abc1904d8> in next(self)
      10 def next(self):
      11 if self.ind == len(self.data):
      ---> 12 raise StopIteration
      13 else:
      14 data = self.data[self.ind]

      next函數(shù)中只能向前取數(shù)據(jù),一次取一個(gè)可以看出來(lái),不過(guò)不能重復(fù)取數(shù)據(jù),那這個(gè)可不可以解決呢? 我們知道iterator只能迭代一次,但是iterable對(duì)象則沒(méi)有這個(gè)限制,因此我們可以把iterator從數(shù)據(jù)中分離出來(lái),分別定義一個(gè)iterableiterator如下:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      class Data(object): # 只是iterable:可迭代對(duì)象而不iterator:迭代器
      def __init__(self, *args):
      self.data = list(args)
      def __iter__(self): # 并沒(méi)有返回自身
      return DataIterator(self)
      class DataIterator(object): # iterator: 迭代器
      def __init__(self, data):
      self.data = data.data
      self.ind = 0
      def __iter__(self):
      return self
      def next(self):
      if self.ind == len(self.data):
      raise StopIteration
      else:
      data = self.data[self.ind]
      self.ind += 1
      return data
      if __name__ == '__main__':
      d = Data(1, 2, 3)
      for x in d:
      print x,
      for x in d:
      print x,

      輸出就是:

      1
      2
      1,2,3
      1,2,3

      可以看出來(lái)數(shù)據(jù)可以復(fù)用,因?yàn)槊看味挤祷匾粋€(gè)DataIterator,但是數(shù)據(jù)卻可以這樣使用,這種實(shí)現(xiàn)方式很常見(jiàn),比如xrange的實(shí)現(xiàn)便是這種數(shù)據(jù)與迭代分離的形式,但是很節(jié)省內(nèi)存,如下:

      1
      2
      3
      4
      5
      In [8]: sys.getsizeof(range(1000000))
      Out[8]: 8000072
      In [9]: sys.getsizeof(xrange(1000000))
      Out[9]: 40

      另外有個(gè)小tips, 就是為什么可以使用for 迭代迭代器對(duì)象,原因就是for替我們做了next的活,以及接收StopIteration的處理。 迭代器大概就記錄到這里了,下面開(kāi)始一個(gè)特殊的更加優(yōu)雅的迭代器: 生成器

      生成器(generator)

      首先需要明確的就是生成器也是iterator迭代器,因?yàn)樗裱说鲄f(xié)議.

      兩種創(chuàng)建方式

      包含yield的函數(shù)

      生成器函數(shù)跟普通函數(shù)只有一點(diǎn)不一樣,就是把 return 換成yield,其中yield是一個(gè)語(yǔ)法糖,內(nèi)部實(shí)現(xiàn)了迭代器協(xié)議,同時(shí)保持狀態(tài)可以掛起。如下: 記住一點(diǎn),yield是數(shù)據(jù)的生產(chǎn)者,而諸如for等是數(shù)據(jù)的消費(fèi)者。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      def gen():
      print 'begin: generator'
      i = 0
      while True:
      print 'before return ', i
      yield i
      i += 1
      print 'after return ', i
      a = gen()
      In [10]: a #只是返回一個(gè)對(duì)象
      Out[10]: <generator object gen at 0x7f40c33adfa0>
      In [11]: a.next() #開(kāi)始執(zhí)行
      begin: generator
      before return 0
      Out[11]: 0
      In [12]: a.next()
      after return 1
      before return 1
      Out[12]: 1

      首先看到while True 不必驚慌,它只會(huì)一個(gè)一個(gè)的執(zhí)行~ 看結(jié)果可以看出一點(diǎn)東西: - 調(diào)用gen()并沒(méi)有真實(shí)執(zhí)行函數(shù),而是只是返回了一個(gè)生成器對(duì)象 - 執(zhí)行第一次a.next()時(shí),才真正執(zhí)行函數(shù),執(zhí)行到yield一個(gè)返回值,然后就會(huì)掛起,保持當(dāng)前的名字空間等狀態(tài)。然后等待下一次的調(diào)用,從yield的下一行繼續(xù)執(zhí)行。

      還有一種情況也會(huì)執(zhí)行生成器函數(shù),就是當(dāng)檢索生成器的元素時(shí),如list(generator), 說(shuō)白了就是當(dāng)需要數(shù)據(jù)的時(shí)候,才會(huì)執(zhí)行。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      In [15]: def func():
      ....: print 'begin'
      ....: for i in range(4):
      ....: yield i
      In [16]: a = func()
      In [17]: list(a) #檢索數(shù)據(jù),開(kāi)始執(zhí)行
      begin
      Out[17]: [0, 1, 2, 3]

      yield還有其他高級(jí)應(yīng)用,后面再慢慢學(xué)習(xí)。

      生成器表達(dá)式

      列表生成器十分方便:如下,求10以內(nèi)的奇數(shù): [i for i in range(10) if i % 2]

      同樣在python 2.4也引入了生成器表達(dá)式,而且形式非常類似,就是把[]換成了().

      1
      2
      3
      4
      5
      6
      7
      In [18]: a = ( i for i in range(4))
      In [19]: a
      Out[19]: <generator object <genexpr> at 0x7f40c2cfe410>
      In [20]: a.next()
      Out[20]: 0

      可以看出生成器表達(dá)式創(chuàng)建了一個(gè)生成器,而且生有個(gè)特點(diǎn)就是惰性計(jì)算, 只有在被檢索時(shí)候,才會(huì)被賦值。 之前有篇文章:python 默認(rèn)參數(shù)問(wèn)題及一個(gè)應(yīng)用,最后有一個(gè)例子:

      1
      2
      3
      def multipliers():
      return (lambda x : i * x for i in range(4)) #修改成生成器
      print [m(2) for m in multipliers()]

      這個(gè)就是說(shuō),只有在執(zhí)行m(2)的時(shí)候,生成器表達(dá)式里面的for才會(huì)開(kāi)始從0循環(huán),然后接著才是i * x,因此不存在那篇文章中的問(wèn)題. 惰性計(jì)算這個(gè)特點(diǎn)很有用,上述就是一個(gè)應(yīng)用,2gua這樣說(shuō)的: >惰性計(jì)算想像成水龍頭,需要的時(shí)候打開(kāi),接完水了關(guān)掉,這時(shí)候數(shù)據(jù)流就暫停了,再需要的時(shí)候再打開(kāi)水龍頭,這時(shí)候數(shù)據(jù)仍是接著輸出,不需要從頭開(kāi)始循環(huán)

      個(gè)人理解就是就是可以利用生成器來(lái)作為數(shù)據(jù)管道使用,當(dāng)被檢索的時(shí)候,每次拿出一個(gè)數(shù)據(jù),然后向下面?zhèn)鬟f,傳到最后,再拿第二個(gè)數(shù)據(jù),在下面的例子中會(huì)詳細(xì)說(shuō)明。 其實(shí)本質(zhì)跟迭代器差不多,不一次性把數(shù)據(jù)都那過(guò)來(lái),需要的時(shí)候,才拿。

      回到例子

      看到這里,開(kāi)始的例子應(yīng)該大概可以有點(diǎn)清晰了, 核心語(yǔ)句就是:

      1
      2
      3
      4
      5
      def gen():
      for i in range(4):
      yield i
      for n in [1, 10]:
      base = (add(i, n) for i in base)

      之前的解釋有點(diǎn)瑕疵,容易誤導(dǎo)對(duì)生成器的理解 在執(zhí)行list(base)的時(shí)候,開(kāi)始檢索,然后生成器開(kāi)始運(yùn)算了。關(guān)鍵是,這個(gè)循環(huán)次數(shù)是2,也就是說(shuō),有兩次生成器表達(dá)式的過(guò)程。必須牢牢把握住這一點(diǎn)。 生成器返回去開(kāi)始運(yùn)算,n = 10而不是1沒(méi)問(wèn)題吧,這個(gè)在上面提到的文章中已經(jīng)提到了,就是add(i+n)綁定的是n這個(gè)變量,而不是它當(dāng)時(shí)的數(shù)值。 然后首先是第一次生成器表達(dá)式的執(zhí)行過(guò)程:base = (10 + 0, 10 + 1, 10 + 2, 10 +3),這是第一次循環(huán)的結(jié)果(形象表示,其實(shí)已經(jīng)計(jì)算出來(lái)了(10,11,12,3)), 然后第二次,base = (10 + 10, 11 + 10, 12 + 10, 13 + 10) ,終于得到結(jié)果了[20, 21, 22, 23].

      這個(gè)可以以管道的思路來(lái)理解,首先gen()函數(shù)是第一個(gè)生成器,下一個(gè)是第一次循環(huán)的base = (add(i, n) for i in base),最后一個(gè)生成器是第二次循環(huán)的base = (add(i, n) for i in base)。 這樣就相當(dāng)于三個(gè)管道依次連接,但是水(數(shù)據(jù))還沒(méi)有流過(guò),現(xiàn)在到了list(base),就相當(dāng)于驅(qū)動(dòng)器,打開(kāi)了水的開(kāi)關(guān),這時(shí)候,按照管道的順序,由第一個(gè)產(chǎn)生一個(gè)數(shù)據(jù),yield 0,然后第一個(gè)管道關(guān)閉。 之后傳遞給第二個(gè)管道就是第一次循環(huán),此時(shí)執(zhí)行了add(0, 10),然后水繼續(xù)流,到第二次循環(huán),再執(zhí)行add(10, 10),此時(shí)到管道尾巴了,第一個(gè)數(shù)據(jù)20就產(chǎn)生了。此時(shí)第一個(gè)管道再開(kāi)放yield 1, 流程跟上面的一樣。依次產(chǎn)生21,22,23; 直到?jīng)]有數(shù)據(jù)。 把代碼改一下容易理解:

      1
      2
      3
      4
      5
      6
      7
      8
      def gen():
      for i in range(4):
      yield i # 第一個(gè)管道
      base = (add(i, 10) for i in base) # 第二個(gè)管道
      base = (add(i, 10) for i in base) # 第三個(gè)管道
      list(base) # 開(kāi)關(guān)驅(qū)動(dòng)器

      具體執(zhí)行過(guò)程可以在pythontutor上看看. 之前的解釋被誤導(dǎo)的原因是,可能會(huì)誤以為是在第二個(gè)管道就把gen()執(zhí)行完畢了,其實(shí)不是這樣的。 這種寫(xiě)法的好處顯而易見(jiàn):內(nèi)存占用低。在數(shù)據(jù)量極大的時(shí)候,用list就只能爆內(nèi)存,而用生成器模式則完全不用擔(dān)心

      小結(jié)

      概括

      主要介紹了大概這樣幾點(diǎn): - iterable,iteratoritertion的概念 - 迭代器協(xié)議 - 自定義可迭代對(duì)象與迭代器分離,保證數(shù)據(jù)復(fù)用 - 生成器: 特殊的迭代器,內(nèi)部實(shí)現(xiàn)了迭代器協(xié)議

      其實(shí)這一塊, 那幾個(gè)概念搞清楚, ,這個(gè)很關(guān)鍵, 搞懂了后面就水到渠成了。而且對(duì)之前的知識(shí)也有很多加深。 比如常見(jiàn)list就是iteratoriteable分離實(shí)現(xiàn)的,本身是可迭代對(duì)象,但不是迭代器, 類似與xrange,但是又不同。 越來(lái)越明白,看源碼的重要性了。

      參考

      • http://www./2012/01/understanding-python-iterables-and.html
      • http://www./2009/02/23/iterators-iterables-and-generators-oh-my/
      • http:///questions/9884132/what-exactly-are-pythons-iterator-iterable-and-iteration-protocols
      • http://python./81881/

        本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評(píng)論

        發(fā)表

        請(qǐng)遵守用戶 評(píng)論公約

        類似文章 更多