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

資訊專欄INFORMATION COLUMN

python迭代器與生成器小結

hellowoody / 844人閱讀

摘要:迭代器要說生成器,必須首先說迭代器區分與講到迭代器,就需要區別幾個概念看著都差不多,其實不然。比如常見就是與分離實現的本身是可迭代對象,但不是迭代器,類似與但是又不同。

2016.3.10關于例子解釋的補充更新

源自我的博客

例子

老規矩,先上一個代碼:

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)

這個東西輸出可以腦補一下, 結果是[20,21,22,23], 而不是[10, 11, 12, 13]。 當時糾結了半天,一直沒搞懂,后來齊老師稍微指點了一下, 突然想明白了--真夠笨的,唉。。好了--正好趁機會稍微小結一下python里面的生成器。

迭代器(iterator)

要說生成器,必須首先說迭代器

區分iterable,iterator與itertion

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

itertion: 就是迭代,一個接一個(one after another),是一個通用的概念,比如一個循環遍歷某個數組。

iterable: 這個是可迭代對象,屬于python的名詞,范圍也很廣,可重復迭代,滿足如下其中之一的都是iterable:

可以for循環: for i in iterable

可以按index索引的對象,也就是定義了__getitem__方法,比如list,str;

定義了__iter__方法。可以隨意返回。

可以調用iter(obj)的對象,并且返回一個iterator

iterator: 迭代器對象,也屬于python的名詞,只能迭代一次。需要滿足如下的迭代器協議

定義了__iter__方法,但是必須返回自身

定義了next方法,在python3.x是__next__。用來返回下一個值,并且當沒有數據了,拋出StopIteration

可以保持當前的狀態

首先str和listiterable 但不是iterator:

In [3]: s = "hi"

In [4]: s.__getitem__
Out[4]: 

In [5]: s.next # 沒有next方法
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
 in ()
----> 1 s.next

AttributeError: "str" object has no attribute "next"

In [6]: l = [1,2] # 同理

In [7]: l.__iter__
Out[7]: 

In [8]: l.next
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
 in ()
----> 1 l.next

AttributeError: "list" object has no attribute "next"
In [9]: iter(s) is s #iter() 沒有返回本身
Out[9]: False
In [10]: iter(l) is l #同理
Out[10]: False

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

In [13]: si = iter(s)

In [14]: si
Out[14]: 

In [15]: si.__iter__ # 有__iter__
Out[15]: 

In [16]: si.next #擁有next
Out[16]: 

In [20]: si.__iter__() is si #__iter__返回自己
Out[20]: True

這樣,由這幾個例子可以解釋清楚這幾個概念的區別。

自定義iterator 與數據分離

說到這里,迭代器對象基本出來了。下面大致說一下,如何讓自定義的類的對象成為迭代器對象,其實就是定義__iter__next方法:

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): # 返回數據
        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: # 開始迭代
   ....:     print x
   ....:
1
2

In [13]: d.next() # 只能迭代一次,再次使用則會拋異常
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
----> 1 d.next()
 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函數中只能向前取數據,一次取一個可以看出來,不過不能重復取數據,那這個可不可以解決呢?

我們知道iterator只能迭代一次,但是iterable對象則沒有這個限制,因此我們可以把iterator從數據中分離出來,分別定義一個iterableiterator如下:

class Data(object):   # 只是iterable:可迭代對象而不iterator:迭代器

    def __init__(self, *args):
        self.data = list(args)

    def __iter__(self):  # 并沒有返回自身
        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,3
1,2,3

可以看出來數據可以復用,因為每次都返回一個DataIterator,但是數據卻可以這樣使用,這種實現方式很常見,比如xrange的實現便是這種數據與迭代分離的形式,但是很節省內存,如下:

In [8]: sys.getsizeof(range(1000000))
Out[8]: 8000072

In [9]: sys.getsizeof(xrange(1000000))
Out[9]: 40

另外有個小tips, 就是為什么可以使用for 迭代迭代器對象,原因就是for替我們做了next的活,以及接收StopIteration的處理。

迭代器大概就記錄到這里了,下面開始一個特殊的更加優雅的迭代器: 生成器

生成器(generator)

首先需要明確的就是生成器也是iterator迭代器,因為它遵循了迭代器協議.

兩種創建方式 包含yield的函數

生成器函數跟普通函數只有一點不一樣,就是把 return 換成yield,其中yield是一個語法糖,內部實現了迭代器協議,同時保持狀態可以掛起。如下:
記住一點,yield是數據的生產者,而諸如for等是數據的消費者。

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 #只是返回一個對象
Out[10]: 

In [11]: a.next() #開始執行
begin: generator
before return  0
Out[11]: 0

In [12]: a.next()
after return  1
before return  1
Out[12]: 1

首先看到while True 不必驚慌,它只會一個一個的執行~
看結果可以看出一點東西:

調用gen()并沒有真實執行函數,而是只是返回了一個生成器對象

執行第一次a.next()時,才真正執行函數,執行到yield一個返回值,然后就會掛起,保持當前的名字空間等狀態。然后等待下一次的調用,從yield的下一行繼續執行。

還有一種情況也會執行生成器函數,就是當檢索生成器的元素時,如list(generator), 說白了就是當需要數據的時候,才會執行。

In [15]: def func():
   ....:     print "begin"
   ....:     for i in range(4):
   ....:         yield i

In [16]: a = func()

In [17]: list(a) #檢索數據,開始執行
begin
Out[17]: [0, 1, 2, 3]

yield還有其他高級應用,后面再慢慢學習。

生成器表達式

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

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

In [18]: a = ( i for i in range(4))

In [19]: a
Out[19]:  at 0x7f40c2cfe410>

In [20]: a.next()
Out[20]: 0

可以看出生成器表達式創建了一個生成器,而且生有個特點就是惰性計算, 只有在被檢索時候,才會被賦值。
之前有篇文章:python 默認參數問題及一個應用,最后有一個例子:

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

這個就是說,只有在執行m(2)的時候,生成器表達式里面的for才會開始從0循環,然后接著才是i * x,因此不存在那篇文章中的問題.
惰性計算這個特點很有用,上述就是一個應用,2gua這樣說的:

惰性計算想像成水龍頭,需要的時候打開,接完水了關掉,這時候數據流就暫停了,再需要的時候再打開水龍頭,這時候數據仍是接著輸出,不需要從頭開始循環

個人理解就是就是可以利用生成器來作為數據管道使用,當被檢索的時候,每次拿出一個數據,然后向下面傳遞,傳到最后,再拿第二個數據,在下面的例子中會詳細說明。
其實本質跟迭代器差不多,不一次性把數據都那過來,需要的時候,才拿。

回到例子

看到這里,開始的例子應該大概可以有點清晰了,
核心語句就是:

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

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

新思路
這個可以以管道的思路來理解,首先gen()函數是第一個生成器,下一個是第一次循環的base = (add(i, n) for i in base),最后一個生成器是第二次循環的base = (add(i, n) for i in base)。
這樣就相當于三個管道依次連接,但是水(數據)還沒有流過,現在到了list(base),就相當于驅動器,打開了水的開關,這時候,按照管道的順序,由第一個產生一個數據,yield 0,然后第一個管道關閉。
之后傳遞給第二個管道就是第一次循環,此時執行了add(0, 10),然后水繼續流,到第二次循環,再執行add(10, 10),此時到管道尾巴了,此時產生了第一個數據20,然后第一個管道再開放:yield 1, 流程跟上面的一樣,依次產生21,22,23; 直到沒有數據。
把代碼改一下容易理解:

def gen():
    for i in range(4):
        yield i  #  第一個管道

base = (add(i, 10) for i in base) #  第二個管道
base = (add(i, 10) for i in base) #  第三個管道

list(base) #  開關驅動器

具體執行過程可以在pythontutor上:
之前的解釋被誤導的原因是,可能會誤以為是在第二個管道就把gen()執行完畢了,其實不是這樣的。
這種寫法的好處顯而易見:內存占用低。在數據量極大的時候,用list就只能爆內存,而用生成器模式則完全不用擔心

小結 概括

主要介紹了大概這樣幾點:

iterable,iteratoritertion的概念

迭代器協議

自定義可迭代對象與迭代器分離,保證數據復用

生成器: 特殊的迭代器,內部實現了迭代器協議

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

參考

http://www.shutupandship.com/2012/01/understanding-python-iterables-and.html

http://www.learningpython.com/2009/02/23/iterators-iterables-and-generators-oh-my/

http://stackoverflow.com/questions/9884132/what-exactly-are-pythons-iterator-iterable-and-iteration-protocols

http://python.jobbole.com/81881/

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/37800.html

相關文章

  • Python進階:設計模式之迭代器模式

    摘要:抓住了迭代器模式的本質,即是迭代,賦予了它極高的地位。輸出結果輸出結果小結迭代器模式幾乎是種設計模式中最常用的設計模式,本文主要介紹了是如何運用迭代器模式,并介紹了模塊生成迭代器的種方法,以及種生成迭代器的內置方法。 showImg(https://segmentfault.com/img/bVbmv7W?w=4272&h=2848); 在軟件開發領域中,人們經常會用到這一個概念——設...

    pubdreamcc 評論0 收藏0
  • Python進階:設計模式之迭代器模式

    摘要:抓住了迭代器模式的本質,即是迭代,賦予了它極高的地位。輸出結果輸出結果小結迭代器模式幾乎是種設計模式中最常用的設計模式,本文主要介紹了是如何運用迭代器模式,并介紹了模塊生成迭代器的種方法,以及種生成迭代器的內置方法。 showImg(https://segmentfault.com/img/bVbmv7W?w=4272&h=2848); 在軟件開發領域中,人們經常會用到這一個概念——設...

    sherlock221 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<