1. 程式人生 > >Python知識點-生成器和迭代器

Python知識點-生成器和迭代器

1.先了解下列表生成式

有一個列表[1,2,3,4,5,6,7,8,9],對沒一個元素都進行*2

#通用做法是用lamba
ret = map(lambda x:x*2,[1,2,3,4,5,6,7,8,9])
#python3 得到的ret 是一個map物件,在通過for迴圈得到
#python2 得到的事一個列表不需要再次for迴圈
print(ret)  #<map object at 0x104ef66d8>
for i in ret:
    print(i)

#通過列表生成式直接生成一個列表
ret = [x*2 for x  in [1,2,3,4,5,6,7,8,9]]

2.什麼是生成器?

通過列表生成式,我們可以直接建立一個列表,但是,受到記憶體限制,列表容量肯定是有限的,而且建立一個包含100萬個元素的列表,不僅佔用很大的儲存空間,如果我們僅僅需要訪問前面幾個元素,就會浪費大量的記憶體空間。

所以,如果列表元素可以按照某種演算法推算出來,那我們是否可以在迴圈的過程中不斷推算出後續的元素呢?這樣就不必建立完整的list,從而節省大量的空間,在Python中,這種一邊迴圈一邊計算的機制,稱為生成器:generator

生成器是一個特殊的程式,可以被用作控制迴圈的迭代行為,python中生成器是迭代器的一種,使用yield返回值函式,每次呼叫yield會暫停,而可以使用next()函式和send()函式恢復生成器。

3.python中的生成器,生成器表示式:返回一個物件,這個物件只有在需要的時候才產生結果

#要建立一個generator,有很多種方法,第一種方法很簡單,只有把一個列表生成式的[]中括號改為()小括號,就建立一個generator

#列表生成式
lis = [x*x for x in range(10)]
print(lis)
#生成器
generator_ex = (x*x for x in range(10))
print(generator_ex)
 
結果:
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
<generator object <genexpr> at 0x000002A4CBF9EBA0>
#
得到的結果也不一樣,一個是列表一個是物件,那麼如何打印出generator_ex的每一個元素呢?通過generator_ex.__next__()或者next(generator_ex) 結果: 0 1 4 9 16 25 36 49 64 81 Traceback (most recent call last): File "列表生成式.py", line 42, in <module> print(next(generator_ex)) StopIteration #到最後一個元素後,再次呼叫__next__() 會觸發 StopIteration error,代表已經沒有元素了,這種感覺很不好,所以一般我們使用for 迴圈拿就不會觸發error,因為generator也是可迭代物件 for i in generator_ex: print(i)

3.生成器函式使用以斐波那切數列舉例,從第3項開始,每一項都等於前兩項之和。1 1 2 3 5 8 13 21 34...... 

生成器函式:也是用def定義的,利用關鍵字yield一次性返回一個結果,阻塞,重新開始

#斐波那契數列,普通函式形式
def fib(number):
    n=0
    a,b=0,1
    while n<number:
        a,b=b,a+b
        n=n+1
        print(a)
    return ('done')
a = fib(10)
print(a)

# 通過生成器實現,每次呼叫__next__() 都會yield d 拿到b 這個值,然後繼續迴圈到yiled b停住,繼續監聽有沒有next呼叫
def fib(number):
    n=0
    a,b=0,1
    while n <number:
        yield b
        a,b=b,a+b
        n=n+1
    return('done')

f=fib(3)
# 通過for迴圈拿不到return值
for i in f:
    print(i)

#結果:
1
1
2
3
5
8
13
21
34
55
#要是想得到return 返回值就需要__next__()通過try except獲取異常
def fib(number):
    n,a,b =0,0,1
    while n < number:
        yield b
        a,b =b,a+b
        n = n+1
    return('done')
g = fib(6)
while True:
    try:
        x = __next__(g)
        print('generator: ',x)
    except StopIteration as e:
        print("生成器返回值:",e.value)
        break
 
 
結果:
generator:  1
generator:  1
generator:  2
generator:  3
generator:  5
generator:  8
生成器返回值: done

4. 生成器 send 用法

def generator():
    while True:
        receive = yield 1
        print('hahaha ' + str(receive))
        yield 2


g = generator()
print(next(g))  #一定要先next一次後再send值,不然會報錯
print(g.send('ok')) #send ok 給receive接收,receive = yield 1 這句就代表結束了,然後遇到下一次的yiled斷點 返回yiled的值
# print(next(g))


#結果:
1
hahaha ok
2
1

5.通過 generator yiled 來實現協程,標準的生產者-消費者模型

import time

def consumer():
    r = ''
    while True:
        r = '我餓了'
        n = yield r #這個n 是生產者傳送過來的n 不是r 這個值
        print(n)
        if not n:
            return
        print('[消費者] 消費了 %s 個大包子...' % n)
        # time.sleep(1)

# c = consumer()
# print(c.__next__())
# print(c.send('a'))

def produce(c):
    # 消費者呼叫next 準備send包子
    print(c.__next__())
    n = 0
    #設定生產幾個包子
    while n < 5:
        n = n + 1
        print('[生產者] 生產了 %s 個大包子...' % n)
        r = c.send(n) #send給 消費者 並且接收了yiled '我餓了'
        if r == '我餓了':
            print('[生產者] 消費者說真他媽好吃啊')
        else:
            print('[生產者] 消費者說狗都不想吃')
    c.close()

if __name__=='__main__':
    c = consumer() #消費者例項
    produce(c)

#結果:
我餓了
[生產者] 生產了 1 個大包子...
[消費者] 消費了 1 個大包子...
[生產者] 消費者說真他媽好吃啊
[生產者] 生產了 2 個大包子...
[消費者] 消費了 2 個大包子...
[生產者] 消費者說真他媽好吃啊
[生產者] 生產了 3 個大包子...
[消費者] 消費了 3 個大包子...
[生產者] 消費者說真他媽好吃啊
[生產者] 生產了 4 個大包子...
[消費者] 消費了 4 個大包子...
[生產者] 消費者說真他媽好吃啊
[生產者] 生產了 5 個大包子...
[消費者] 消費了 5 個大包子...
[生產者] 消費者說真他媽好吃啊

6.迭代器

迭代器就是迴圈,並且可以用next呼叫
可迭代物件Iterable不一定是Iterator迭代器,但是迭代器一定時可迭代物件。
生成器都是迭代器Iterator,但list、dict、str雖然是Iterable(可迭代物件),卻不是Iterator(迭代器),可以使用for 迴圈的都是可迭代物件。

#判斷是否為可迭代物件
>>> from collections import Iterable
>>> isinstance([], Iterable)
True
>>> isinstance({}, Iterable)
True
>>> isinstance('abc', Iterable)
True
>>> isinstance((x for x in range(10)), Iterable)
True
>>> isinstance(100, Iterable)
False

#也可以使用isinstance()判斷一個物件是否是迭代器Iterator物件:

>>> from collections import Iterator
>>> isinstance((x for x in range(10)), Iterator)
True
>>> isinstance([], Iterator)
False
>>> isinstance({}, Iterator)
False
>>> isinstance('abc', Iterator)
False

#可以使用iter()變成迭代器物件
>>> isinstance(iter([]), Iterator)
True
>>> isinstance(iter('abc'), Iterator)
True

>>> a=[1,2,3,4]
>>> b=iter(a)
>>> print(b)
<list_iterator object at 0x103c7e6d8>
>>> b.__next__()
1
>>> b.__next__()
2
>>> b.__next__()
3
>>> b.__next__()
#要判斷一個物件是不是迭代器 可以直接用next方法呼叫,或者用isinstance(物件,Iterator)