午夜国产狂喷潮在线观看|国产AⅤ精品一区二区久久|中文字幕AV中文字幕|国产看片高清在线

    Python的迭代器和生成器怎么樣
    來(lái)源:易賢網(wǎng) 閱讀:1051 次 日期:2015-03-23 15:36:19
    溫馨提示:易賢網(wǎng)小編為您整理了“Python的迭代器和生成器怎么樣”,方便廣大網(wǎng)友查閱!

    迭代器是訪問(wèn)集合元素的一種方式。迭代器對(duì)象從集合的第一個(gè)元素開(kāi)始訪問(wèn),直到所有的元素被訪問(wèn)完結(jié)束。迭代器只能往前不會(huì)后退,不過(guò)這也沒(méi)什么,因?yàn)槿藗兒苌僭诘局型笸恕?/p>

    使用迭代器的優(yōu)點(diǎn)

    對(duì)于原生支持隨機(jī)訪問(wèn)的數(shù)據(jù)結(jié)構(gòu)(如tuple、list),迭代器和經(jīng)典for循環(huán)的索引訪問(wèn)相比并無(wú)優(yōu)勢(shì),反而丟失了索引值(可以使用內(nèi)建函數(shù)enumerate()找回這個(gè)索引值)。但對(duì)于無(wú)法隨機(jī)訪問(wèn)的數(shù)據(jù)結(jié)構(gòu)(比如set)而言,迭代器是唯一的訪問(wèn)元素的方式。

    另外,迭代器的一大優(yōu)點(diǎn)是不要求事先準(zhǔn)備好整個(gè)迭代過(guò)程中所有的元素。迭代器僅僅在迭代到某個(gè)元素時(shí)才計(jì)算該元素,而在這之前或之后,元素可以不存在或者被銷(xiāo)毀。這個(gè)特點(diǎn)使得它特別適合用于遍歷一些巨大的或是無(wú)限的集合,比如幾個(gè)G的文件,或是斐波那契數(shù)列等等。

    迭代器更大的功勞是提供了一個(gè)統(tǒng)一的訪問(wèn)集合的接口,只要定義了__iter__()方法對(duì)象,就可以使用迭代器訪問(wèn)。

    迭代器有兩個(gè)基本的方法

    next方法:返回迭代器的下一個(gè)元素

    __iter__方法:返回迭代器對(duì)象本身

    一、迭代器Iterators

    迭代器僅是一容器對(duì)象,它實(shí)現(xiàn)了迭代器協(xié)議。它有兩個(gè)基本方法:

    1)next方法

    返回容器的下一個(gè)元素

    2)__iter__方法

    返回迭代器自身

    迭代器可使用內(nèi)建的iter方法創(chuàng)建,見(jiàn)例子:

    >>> i = iter('abc')

    >>> i.next()

    'a'

    >>> i.next()

    'b'

    >>> i.next()

    'c'

    >>> i.next()

    Traceback (most recent call last):

    File "", line 1, in

    StopIteration:

    class MyIterator(object):

    def __init__(self, step):

    self.step = step

    def next(self):

    """Returns the next element."""

    if self.step==0:

    raise StopIteration

    self.step-=1

    return self.step

    def __iter__(self):

    """Returns the iterator itself."""

    return self

    for el in MyIterator(4):

    print el

    --------------------

    結(jié)果:

    3

    2

    1

    0

    二、生成器Generators

    從Python2.2起,生成器提供了一種簡(jiǎn)潔的方式幫助返回列表元素的函數(shù)來(lái)完成簡(jiǎn)單和有效的代碼。

    它基于yield指令,允許停止函數(shù)并立即返回結(jié)果。

    此函數(shù)保存其執(zhí)行上下文,如果需要,可立即繼續(xù)執(zhí)行。

    例如Fibonacci函數(shù):

    def fibonacci():

    a,b=0,1

    while True:

    yield b

    a,b = b, a+b

    fib=fibonacci()

    print fib.next()

    print fib.next()

    print fib.next()

    print [fib.next() for i in range(10)]

    --------------------

    結(jié)果:

    1

    1

    2

    [3, 5, 8, 13, 21, 34, 55, 89, 144, 233]

    PEP Python Enhancement Proposal Python增強(qiáng)建議

    tokenize模塊

    >>> import tokenize

    >>> reader = open('c:/temp/py1.py').next

    >>> tokens=tokenize.generate_tokens(reader)

    >>> tokens.next()

    (1, 'class', (1, 0), (1, 5), 'class MyIterator(object):/n')

    >>> tokens.next()

    (1, 'MyIterator', (1, 6), (1, 16), 'class MyIterator(object):/n')

    >>> tokens.next()

    (51, '(', (1, 16), (1, 17), 'class MyIterator(object):/n')

    例子:

    def power(values):

    for value in values:

    print 'powering %s' %value

    yield value

    def adder(values):

    for value in values:

    print 'adding to %s' %value

    if value%2==0:

    yield value+3

    else:

    yield value+2

    elements = [1,4,7,9,12,19]

    res = adder(power(elements))

    print res.next()

    print res.next()

    --------------------

    結(jié)果:

    powering 1

    adding to 1

    3

    powering 4

    adding to 4

    7

    保持代碼簡(jiǎn)單,而不是數(shù)據(jù)。

    注意:寧可有大量簡(jiǎn)單的可迭代函數(shù),也不要一個(gè)復(fù)雜的一次只計(jì)算出一個(gè)值的函數(shù)。

    例子:

    def psychologist():

    print 'Please tell me your problems'

    while True:

    answer = (yield)

    if answer is not None:

    if answer.endswith('?'):

    print ("Don't ask yourself too much questions")

    elif 'good' in answer:

    print "A that's good, go on"

    elif 'bad' in answer:

    print "Don't be so negative"

    free = psychologist()

    print free.next()

    print free.send('I feel bad')

    print free.send("Why I shouldn't ?")

    print free.send("ok then i should find what is good for me")

    --------------------

    結(jié)果:

    Please tell me your problems

    None

    Don't be so negative

    None

    Don't ask yourself too much questions

    None

    A that's good, go on

    None

    雖然很早之前就接觸yield這個(gè)詞了,卻一直是一知半解。趁現(xiàn)在有時(shí)間,把它研究一通再說(shuō)。

    含有yield的函數(shù)說(shuō)明它是一個(gè)生成器,而不是普通的函數(shù)。當(dāng)程序運(yùn)行到y(tǒng)ield這一行時(shí),該函數(shù)會(huì)返回值,并保存當(dāng)前域的所有變量狀態(tài);等到該函數(shù)下一次被調(diào)用時(shí),會(huì)從上一次中斷的地方開(kāi)始執(zhí)行,一直遇到下一個(gè)yield, 程序返回值, 并在此保存當(dāng)前狀態(tài); 如此反復(fù),直到函數(shù)正常執(zhí)行完成。

    我一開(kāi)始還想不明白調(diào)用者與生成器之間的函數(shù)堆棧是怎么做到的,后來(lái)才大悟原來(lái)是用到了'協(xié)程'這個(gè)原理。協(xié)程可視為微線程,下面會(huì)結(jié)合例子來(lái)說(shuō)明一下yield及協(xié)程的運(yùn)行過(guò)程。假設(shè)定義了test方法:

    [python]

    def test(len):

    i = 0

    while i < len :

    yield i

    i += 1

    我們來(lái)調(diào)用它看看輸出:

    >>> for i in test(5):

    print i

    輸出:

    0

    1

    2

    3

    4

    這場(chǎng)景是不是很類(lèi)似 for i in xrange(len); 是的, xrange就是這么干的。 for .. in 的操作實(shí)際上是調(diào)用了生成器的next()方法,以上的調(diào)用過(guò)程可以等價(jià)為:

    [python]

    f = test(5)

    print f.next()

    print f.next()

    print f.next()

    print f.next()

    print f.next()

    輸出結(jié)果與上次輸出一致。

    另外,在這次調(diào)用過(guò)程中,協(xié)程被創(chuàng)建了一次, 被喚醒了5次(通過(guò)next),被掛起了5次(通過(guò)yield), 最后協(xié)程退出并銷(xiāo)毀。 大概就這些點(diǎn)了,有更深的理解再做補(bǔ)充。

    生成器(Generator)

    如果列表元素可以按照某種算法推算出來(lái),那我們是否可以在循環(huán)的過(guò)程中不斷推算出后續(xù)的元素呢?這樣就不必創(chuàng)建完整的list,從而節(jié)省大量的空間。在Python中,這種一邊循環(huán)一邊計(jì)算的機(jī)制,稱(chēng)為生成器(Generator)。

    要?jiǎng)?chuàng)建一個(gè)generator,有很多種方法。第一種方法很簡(jiǎn)單,只要把一個(gè)列表生成式的[]改成(),就創(chuàng)建了一個(gè)generator:

    >>> L = [x * x for x in range(10)]

    >>> L

    [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

    >>> g = (x * x for x in range(10))

    >>> g

    at 0x104feab40>

    創(chuàng)建L和g的區(qū)別僅在于最外層的[]和(),L是一個(gè)list,而g是一個(gè)generator。

    我們可以直接打印出list的每一個(gè)元素,但我們?cè)趺创蛴〕鰃enerator的每一個(gè)元素呢?

    如果要一個(gè)一個(gè)打印出來(lái),可以通過(guò)generator的next()方法:

    >>> g.next()

    0

    >>> g.next()

    1

    >>> g.next()

    4

    >>> g.next()

    9

    >>> g.next()

    16

    >>> g.next()

    25

    >>> g.next()

    36

    >>> g.next()

    49

    >>> g.next()

    64

    >>> g.next()

    81

    >>> g.next()

    Traceback (most recent call last):

    File "", line 1, in

    StopIteration

    我們講過(guò),generator保存的是算法,每次調(diào)用next(),就計(jì)算出下一個(gè)元素的值,直到計(jì)算到最后一個(gè)元素,沒(méi)有更多的元素時(shí),拋出StopIteration的錯(cuò)誤。

    當(dāng)然,上面這種不斷調(diào)用next()方法實(shí)在是太變態(tài)了,正確的方法是使用for循環(huán),因?yàn)間enerator也是可迭代對(duì)象:

    >>> g = (x * x for x in range(10))

    >>> for n in g:

    ... print n

    ...

    0

    1

    4

    9

    16

    25

    36

    49

    64

    81

    所以,我們創(chuàng)建了一個(gè)generator后,基本上永遠(yuǎn)不會(huì)調(diào)用next()方法,而是通過(guò)for循環(huán)來(lái)迭代它。

    generator非常強(qiáng)大。如果推算的算法比較復(fù)雜,用類(lèi)似列表生成式的for循環(huán)無(wú)法實(shí)現(xiàn)的時(shí)候,還可以用函數(shù)來(lái)實(shí)現(xiàn)。

    比如,著名的斐波拉契數(shù)列(Fibonacci),除第一個(gè)和第二個(gè)數(shù)外,任意一個(gè)數(shù)都可由前兩個(gè)數(shù)相加得到:

    1, 1, 2, 3, 5, 8, 13, 21, 34, ...

    斐波拉契數(shù)列用列表生成式寫(xiě)不出來(lái),但是,用函數(shù)把它打印出來(lái)卻很容易:

    def fib(max):

    n, a, b = 0, 0, 1

    while n < max:

    print b

    a, b = b, a + b

    n = n + 1

    上面的函數(shù)可以輸出斐波那契數(shù)列的前N個(gè)數(shù):

    >>> fib(6)

    1

    1

    2

    3

    5

    8

    仔細(xì)觀察,可以看出,fib函數(shù)實(shí)際上是定義了斐波拉契數(shù)列的推算規(guī)則,可以從第一個(gè)元素開(kāi)始,推算出后續(xù)任意的元素,這種邏輯其實(shí)非常類(lèi)似generator。

    也就是說(shuō),上面的函數(shù)和generator僅一步之遙。要把fib函數(shù)變成generator,只需要把print b改為yield b就可以了:

    def fib(max):

    n, a, b = 0, 0, 1

    while n < max:

    yield b

    a, b = b, a + b

    n = n + 1

    這就是定義generator的另一種方法。如果一個(gè)函數(shù)定義中包含yield關(guān)鍵字,那么這個(gè)函數(shù)就不再是一個(gè)普通函數(shù),而是一個(gè)generator:

    >>> fib(6)

    這里,最難理解的就是generator和函數(shù)的執(zhí)行流程不一樣。函數(shù)是順序執(zhí)行,遇到return語(yǔ)句或者最后一行函數(shù)語(yǔ)句就返回。而變成generator的函數(shù),在每次調(diào)用next()的時(shí)候執(zhí)行,遇到y(tǒng)ield語(yǔ)句返回,再次執(zhí)行時(shí)從上次返回的yield語(yǔ)句處繼續(xù)執(zhí)行。

    舉個(gè)簡(jiǎn)單的例子,定義一個(gè)generator,依次返回?cái)?shù)字1,3,5:

    >>> def odd():

    ... print 'step 1'

    ... yield 1

    ... print 'step 2'

    ... yield 3

    ... print 'step 3'

    ... yield 5

    ...

    >>> o = odd()

    >>> o.next()

    step 1

    1

    >>> o.next()

    step 2

    3

    >>> o.next()

    step 3

    5

    >>> o.next()

    Traceback (most recent call last):

    File "", line 1, in

    StopIteration

    可以看到,odd不是普通函數(shù),而是generator,在執(zhí)行過(guò)程中,遇到y(tǒng)ield就中斷,下次又繼續(xù)執(zhí)行。執(zhí)行3次yield后,已經(jīng)沒(méi)有yield可以執(zhí)行了,所以,第4次調(diào)用next()就報(bào)錯(cuò)。

    回到fib的例子,我們?cè)谘h(huán)過(guò)程中不斷調(diào)用yield,就會(huì)不斷中斷。當(dāng)然要給循環(huán)設(shè)置一個(gè)條件來(lái)退出循環(huán),不然就會(huì)產(chǎn)生一個(gè)無(wú)限數(shù)列出來(lái)。

    同樣的,把函數(shù)改成generator后,我們基本上從來(lái)不會(huì)用next()來(lái)調(diào)用它,而是直接使用for循環(huán)來(lái)迭代:

    >>> for n in fib(6):

    ... print n

    ...

    1

    1

    2

    3

    5

    8

    小結(jié)

    generator是非常強(qiáng)大的工具,在Python中,可以簡(jiǎn)單地把列表生成式改成generator,也可以通過(guò)函數(shù)實(shí)現(xiàn)復(fù)雜邏輯的generator。

    要理解generator的工作原理,它是在for循環(huán)的過(guò)程中不斷計(jì)算出下一個(gè)元素,并在適當(dāng)?shù)臈l件結(jié)束for循環(huán)。對(duì)于函數(shù)改成的generator來(lái)說(shuō),遇到return語(yǔ)句或者執(zhí)行到函數(shù)體最后一行語(yǔ)句,就是結(jié)束generator的指令,for循環(huán)隨之結(jié)束。

    更多信息請(qǐng)查看IT技術(shù)專(zhuān)欄

    更多信息請(qǐng)查看技術(shù)文章
    易賢網(wǎng)手機(jī)網(wǎng)站地址:Python的迭代器和生成器怎么樣
    由于各方面情況的不斷調(diào)整與變化,易賢網(wǎng)提供的所有考試信息和咨詢回復(fù)僅供參考,敬請(qǐng)考生以權(quán)威部門(mén)公布的正式信息和咨詢?yōu)闇?zhǔn)!

    2025國(guó)考·省考課程試聽(tīng)報(bào)名

    • 報(bào)班類(lèi)型
    • 姓名
    • 手機(jī)號(hào)
    • 驗(yàn)證碼
    關(guān)于我們 | 聯(lián)系我們 | 人才招聘 | 網(wǎng)站聲明 | 網(wǎng)站幫助 | 非正式的簡(jiǎn)要咨詢 | 簡(jiǎn)要咨詢須知 | 新媒體/短視頻平臺(tái) | 手機(jī)站點(diǎn) | 投訴建議
    工業(yè)和信息化部備案號(hào):滇ICP備2023014141號(hào)-1 云南省教育廳備案號(hào):云教ICP備0901021 滇公網(wǎng)安備53010202001879號(hào) 人力資源服務(wù)許可證:(云)人服證字(2023)第0102001523號(hào)
    云南網(wǎng)警備案專(zhuān)用圖標(biāo)
    聯(lián)系電話:0871-65099533/13759567129 獲取招聘考試信息及咨詢關(guān)注公眾號(hào):hfpxwx
    咨詢QQ:1093837350(9:00—18:00)版權(quán)所有:易賢網(wǎng)
    云南網(wǎng)警報(bào)警專(zhuān)用圖標(biāo)