(八)迭代器,列表生成式和生成器

2022-06-15 11:42:12 字數 4260 閱讀 7548

對於list、string、tuple、dict等這些容器物件,使用for迴圈遍歷是很方便的。在後台for語句對容器物件呼叫iter()函式。iter()是python內建函式。

iter()函式會返回乙個定義了next()方法的迭代器物件,它在容器中逐個訪問容器內的元素。next()也是python內建函式。在沒有後續元素時,next()會丟擲乙個stopiteration異常,通知for語句迴圈結束。

迭代器是用來幫助我們記錄每次迭代訪問到的位置,當我們對迭代器使用next()函式的時候,迭代器會向我們返回它所記錄位置的下乙個位置的資料。實際上,在使用next()函式的時候,呼叫的就是迭代器物件的_next_方法(python3中是物件的_next_方法,python2中是物件的next()方法)。所以,我們要想構造乙個迭代器,就要實現它的_next_方法。但這還不夠,python要求迭代器本身也是可迭代的,所以我們還要為迭代器實現_iter_方法,而_iter_方法要返回乙個迭代器,迭代器自身正是乙個迭代器,所以迭代器的_iter_方法返回自身self即可。

1,迭代器協議:物件需要提供next()方法,它要麼返回迭代中的下一項,要麼就引起乙個stopiteration異常,以終止迭代。

2,可迭代物件:實現了迭代器協議的物件。list、tuple、dict都不是iterable(可迭代物件)。但可以使用內建函式iter(),把這些都變成iterable(可迭代器物件),以遵循迭代器協議,並擁有next()方法。

3,for item in iterable 迴圈的本質就是先通過iter()函式獲取可迭代物件iterable的迭代器,然後對獲取到的迭代器不斷呼叫next()方法來獲取下乙個值並將其賦值給item,當遇到stopiteration的異常後迴圈結束.

x = '

lan'

iter_test = x.__iter__

() //iter_test就是通過iter()函式獲取的可迭代物件x的迭代器

print

(iter_test) //對獲取到的迭代器不斷呼叫next()方法來獲取下乙個值

print(iter_test.__next__

()) //也可以寫成next(iter_test),__next__()是物件的內建方法,next()是python內建的方法

print(iter_test.__next__

())print(iter_test.__next__()) //如果再呼叫一次next(),將丟擲stopiteration的異常

結果:l

an

以上栗子用for迴圈

x = '

lan'

for i in

x:

print(i)

所以,string、list、tuple、dict這些都不是迭代器物件,只不過在for迴圈中,呼叫了他們內部的__iter__() 方法,使他們變成了迭代器物件,而擁有了next()方法。

用while去模擬以上的for迴圈:

x = '

lan'

i_x = x.__iter__

()while

true:

try:

print(i_x.__next__

())

except

stopiteration:

print('

迭代結束了,迴圈終止')

break

所以當我們使用for迴圈去遍歷物件的時候,本質上是做了兩件事:

1.呼叫物件內部的__iter__() 方法,使他們變成了迭代器物件,對獲取到的迭代器不斷呼叫next()方法,直至取到最後乙個元素。

2.當元素都迭代完了,遇到stopiteration異常的時候,幫我們處理異常。

對於序列型別的物件,像字串、列表、元組等,都有下標,我們可以用下面的方式遍歷:

x = '

lan'

index =0

while index < x.__len__

():

print

(x[index])

index += 1

但是,非序列型別的物件,像字典、集合、檔案物件,就無法用上述方式去遍歷,只能依靠for迴圈,例如對檔案物件的遍歷:

f = open('

123.txt')

i_f = f.__iter__

()print(i_f.__next__(),end=''

)print(i_f.__next__(),end='')

所以,for迴圈就是基於迭代器協議提供了乙個統一的可以遍歷所有物件的方法,即在遍歷之前,先呼叫物件的__iter__()方法使其變成迭代器,然後使用迭代器協議去實現迴圈訪問,這就是無所不能的for迴圈。

a = '

aaa'

res = '哈哈'

if a == '

bbb'

else'嘿嘿

'print(res)

結果:嘿嘿

if a == 'bbb'是第一元,'哈哈' 是第二元,'嘿嘿' 是第三元

如果a == 'bbb',那麼res = '哈哈',否則res = '嘿嘿'

l =

for i in range(10): //普通方式

print

(l)l1 = [i for i in range(10)] //列表生成式

print(l1)

什麼是生成器?

可以理解為一種資料型別,這種資料型別自動實現了迭代器協議(而其他的資料型別需要呼叫自己內建的__iter__方法才能實現迭代器協議),所以生成器本身就是可迭代物件。

迭代器可以節省大量的記憶體空間,通過列表生成式,我們可以直接建立乙個列表。但是,受到記憶體限制,列表容量肯定是有限的。而且,建立乙個包含100萬個元素的列表,不僅占用很大的儲存空間,如果我們僅僅需要訪問前面幾個元素,那後面絕大多數元素占用的空間都白白浪費了。

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

定義generator(生成器)的兩種方法:

#

兩種方式

#函式的方式

deftest():

yield 1 //yield的作用:1.和return一樣返回乙個值,並結束這次函式的執行。 2.儲存當前的狀態,當下一次next(g)或者g.__next__()的時候會從上一次的位置繼續執行,直到遇到下乙個yield

g =test()

print('

來自函式

',g)

#生成器表示式,只要把乙個列表生成式的改成(),就建立了乙個generator

g = (i for i in range(10))

print('

來自生成器表示式

',g)

結果:來自函式

來自生成器表示式

at 0x000002afb98790a0>

既然生成器是一種資料型別,又自動實現了迭代器協議,生成器本身又是可迭代物件,那麼怎麼遍歷生成器呢?三種方式:

def

test():

for i in range(5):

#print(i)

yield

it =test()

print

(t) //這時t就是乙個生成器,但是這只是乙個記憶體位址,裡面沒有任何值,每去遍歷一次,才多乙個值

print

(next(t)) //第一種方式,next(),執行一次多乙個值

print

(next(t))

print

(next(t))

print

(next(t))

print

(next(t))

for i in

t: //第二種方式,用for迴圈去遍歷這個生成器,所有的值挨個取完

print

(i)print(list(t)) //第三種方式,用list、sum、reduce等方式去取,也是取完

結果:

01

234注意:生成器只能遍歷一次!如上方式二和三就沒有取到值,因為方式一已經遍歷了。

列表生成式 生成器 迭代器

1 列表生成式,可以在前邊加入表示式或者函式 a x 2 for x in range 10 print a def f n return n n n b f x for x in range 10 print b 2 a.生成器,每次使用得時候才會計算,每次只能取下乙個而不能跳躍取值.生成器就是乙...

列表生成式 生成器 迭代器

列表生成式 可以直接在列表裡進行運算 生成器 一邊迴圈一邊計算的機制,稱為生成器 generator 就是生成乙個不執行的函式或者列表,即資料流 第二句 生成器與列表的區別 生成器你不知道長度,就是乙個資料流 list dict str你知道長度 迭代器 可以被next 函式呼叫並不斷返回下乙個值的...

Python 列表生成式 生成器 迭代器

常見的列表生成方式 1.list list range 1 5 1 2,3 4 2.for迴圈 l for i in range 1 5 l 1 2,3 4 3.列表生成式 在列表生成時加入限制判斷條件 例 1,2,3,4 x for x in range 1 5 可以新增限制條件 2.4 x fo...