1. 程式人生 > >【Python3之叠代器,生成器】

【Python3之叠代器,生成器】

int clas pen pytho [] fun 異常 recent 開始

一、可叠代對象和叠代器

1.叠代的概念

上一次輸出的結果為下一次輸入的初始值,重復的過程稱為叠代,每次重復即一次叠代,並且每次叠代的結果是下一次叠代的初始值

註:循環不是叠代

while True: #只滿足重復,因而不是叠代
     print(‘====>‘)

2.可叠代的對象

內置__iter__方法的,都是可叠代的對象。

list是可叠代對象,dict是可叠代對象,set也是可叠代對象。

[1,2].__iter__()
‘hello‘.__iter__()
(1,2).__iter__()

{‘a‘:1,‘b‘:2}.__iter__()
{1,2,3}.__iter__()

例如:

技術分享
x = [1, 2, 3]
y = iter(x)
z = iter(x)
print(next(y))
print(next(y))
print(next(z))
print(type(x))
print(type(y))
技術分享

輸出

1
2
1
<class ‘list‘>
<class ‘list_iterator‘>

如下圖所示

這裏x是一個可叠代對象,yz是兩個獨立的叠代器,叠代器內部持有一個狀態,該狀態用於記錄當前叠代所在的位置,以方便下次叠代的時候獲取正確的元素。

叠代器有一種具體的叠代器類型,比如list_iterator

set_iterator。可叠代對象實現了__iter__方法,該方法返回一個叠代器對象。

3.叠代器

  • 1.為什麽要有叠代器?

對於沒有索引的數據類型,必須提供一種不依賴索引的叠代方式。

  • 2.叠代器定義:

叠代器:可叠代對象執行__iter__方法,得到的結果就是叠代器,叠代器對象有__next__方法

它是一個帶狀態的對象,他能在你調用next()方法的時候返回容器中的下一個值,任何實現了__iter____next__()方法的對象都是叠代器,__iter__返回叠代器自身,__next__返回容器中的下一個值,如果容器中沒有更多元素了,則拋出StopIteration異常

  • 3.叠代器的實現

例:

技術分享
i=[1,2,3].__iter__()  

print(i)    #叠代器

print(i.__next__())
print(i.__next__())
print(i.__next__())
#print(i.__next__()) #拋出異常:StopIteration
技術分享

輸出

<list_iterator object at 0x1019c3eb8>
1
2
3

每次調用next()方法的時候做兩件事:

  1. 為下一次調用next()方法修改狀態
  2. 為當前這次調用生成返回結果

叠代器就像一個懶加載的工廠,等到有人需要的時候才給它生成值返回,沒調用的時候就處於休眠狀態等待下一次調用。

  • 4.如何判斷叠代器對象和可叠代對象
技術分享
from collections import Iterable,Iterator
‘abc‘.__iter__()
().__iter__()
[].__iter__()
{‘a‘:1}.__iter__()
{1,2}.__iter__()

f=open(‘a.txt‘,‘w‘)
f.__iter__()

#判斷是否為可叠代對象,以下都是 print(isinstance(‘abc‘,Iterable)) print(isinstance([],Iterable)) print(isinstance((),Iterable)) print(isinstance({‘a‘:1},Iterable)) print(isinstance({1,2},Iterable)) print(isinstance(f,Iterable))
#判斷是否為叠代器,只有文件是 print(isinstance(‘abc‘,Iterator)) print(isinstance([],Iterator)) print(isinstance((),Iterator)) print(isinstance({‘a‘:1},Iterator)) print(isinstance({1,2},Iterator)) print(isinstance(f,Iterator))
技術分享

輸出

技術分享
True
True
True
True
True
True
False
False
False
False
False
True
技術分享

可叠代對象:只有__iter__方法,執行該方法得到的叠代器對象

叠代器:有__iter____next__()方法

註:對於叠代器對象來說,執行__iter__方法,得到的結果仍然是它本身

  • 5.叠代器的優點和缺點

優點:
1.提供了一種不依賴下標的叠代方式
2.就跌叠代器本身來說,更節省內存

缺點:
1. 無法獲取叠代器對象的長度
2. 不如序列類型取值靈活,是一次性的,只能往後取值,不能往前退

二、生成器

1.定義

生成器(generator)是一個特殊的叠代器,它的實現更簡單優雅,yield是生成器實現__next__()方法的關鍵。它作為生成器執行的暫停恢復點,可以對yield表達式進行賦值,也可以將yield表達式的值返回。

也就是說,yield是一個語法糖,內部實現支持了叠代器協議,同時yield內部是一個狀態機,維護著掛起和繼續的狀態。

yield的功能:
1.相當於為函數封裝好__iter__和__next__
2.return只能返回一次值,函數就終止了,而yield能返回多次值,每次返回都會將函數暫停,下一次next會從上一次暫停的位置繼續執行

例:

技術分享
def counter(n):
    print(‘start...‘)
    i=0
    while i < n:
        yield i
        i+=1
    print(‘end...‘)


g=counter(5)
print(g)
print(next(g))
print(next(g))
print(next(g))
print(next(g))
print(next(g))
# print(next(g))   #會報錯
技術分享

輸出

start...
0
1
2
3
4

2.生成器函數

  • 生成器函數:常規函數定義,但是,使用yield語句而不是return語句返回結果。yield語句一次返回一個結果,在每個結果中間,掛起函數的狀態,以便下次重它離開的地方繼續執行;

普通函數return返回

技術分享
def lay_eggs(num):
    egg_list=[]
    for egg in range(num):
        egg_list.append(‘蛋%s‘ %egg)
    return egg_list

yikuangdan=lay_eggs(10) #我們拿到的是蛋
print(yikuangdan)
技術分享

輸出

[‘蛋0‘, ‘蛋1‘, ‘蛋2‘, ‘蛋3‘, ‘蛋4‘, ‘蛋5‘, ‘蛋6‘, ‘蛋7‘, ‘蛋8‘, ‘蛋9‘]

叠代器函數

技術分享
def lay_eggs(num):
    for egg in range(num):
        res=‘蛋%s‘ %egg
        yield res       #生成器關鍵語法
        print(‘下完一個蛋‘)

laomuji=lay_eggs(10) #我們拿到的是一只母雞
print(laomuji)
print(laomuji.__next__())       #叠代  蛋0
print(laomuji.__next__())     #蛋1
print(laomuji.__next__())       #蛋2
egg_l=list(laomuji)
print(egg_l)
技術分享

輸出

技術分享
蛋0
下完一個蛋
蛋1
下完一個蛋
蛋2
下完一個蛋
下完一個蛋
下完一個蛋
下完一個蛋
下完一個蛋
下完一個蛋
下完一個蛋
下完一個蛋
[‘蛋3‘, ‘蛋4‘, ‘蛋5‘, ‘蛋6‘, ‘蛋7‘, ‘蛋8‘, ‘蛋9‘]
技術分享

3.生成器表達式

  • 生成器表達式:類似於列表推導,但是,生成器返回按需產生結果的一個對象,而不是一次構建一個結果列表;
  • food=yield food_list

    #g.send(‘food1‘),先把food1傳給yield,由yield賦值給food,然後返回給food_list,然後再往下執行,直到再次碰到yield,然後把yield後的返回值返回給food_list

註意:開始生成器不能send非空值

技術分享
def eater(name):        #協程函數
    print(‘%s ready to eat‘ %name)
    food_list=[]
    while True:
        food=yield food_list           #裝飾器表達式
        food_list.append(food)
        print(‘%s start to eat %s‘ %(name,food))


g=eater(‘hexin‘)
print(g)        #生成器

print(g.send(‘food1‘)) #傳值
技術分享

輸出

Traceback (most recent call last):
<generator object eater at 0x1049030f8>    #生成器對象
  File "/Users/hexin/PycharmProjects/py3/day5/2.py", line 71, in <module>
    print(g.send(‘food1‘))
TypeError: can‘t send non-None value to a just-started generator    #開始生成器不能send非空值

  • 初始化後
技術分享
def eater(name):        #協程函數
    print(‘%s ready to eat‘ %name)
    food_list=[]
    while True:
        food=yield food_list           #裝飾器表達式
        food_list.append(food)
        print(‘%s start to eat %s‘ %(name,food))


g=eater(‘hexin‘)
print(g)        #生成器
next(g) #等同於 g.send(None),初始化

print(g.send(‘food1‘))
技術分享

輸出

<generator object eater at 0x107cde258>
hexin ready to eat
hexin start to eat food1
[‘food1‘]

  • 為了防止忘記初始化,可利用裝飾器進行初始化,如下
技術分享
def deco(func):     #初始化函數
    def wrapper(*args,**kwargs):
        res=func(*args,**kwargs)
        next(res)          #等同於 g.send(None),初始化
        return res
    return wrapper

@deco       #用初始化函數裝飾器,調用初始化函數
def eater(name):        #協程函數
    print(‘%s ready to eat‘ %name)
    food_list=[]
    while True:
        food=yield food_list           #裝飾器表達式
        food_list.append(food)
        print(‘%s start to eat %s‘ %(name,food))


g=eater(‘hexin‘)
# print(g)        #生成器
# next(g) #等同於 g.send(None),初始化

print(g.send(‘food1‘))
print(g.send(‘food2‘))
print(g.send(‘food3‘))
技術分享

輸出

技術分享
hexin ready to eat
hexin start to eat food1
[‘food1‘]
hexin start to eat food2
[‘food1‘, ‘food2‘]
hexin start to eat food3
[‘food1‘, ‘food2‘, ‘food3‘]
技術分享

【Python3之叠代器,生成器】