初學(xué)時(shí),被可迭代對象,迭代器和生成器繞的云里霧里。
首先來區(qū)分一下這幾個(gè)概念。
迭代器(iterator)
迭代器是指內(nèi)部包含了 iter 和next 方法的類。
iter 函數(shù)返回對象本身,而 next 方法則是返回下一個(gè)元素。
#依次輸出迭代器中的元素
class An_Iterator(object):
def __init__(self,data):
self.data=data
self.index=0
def __iter__(self):
return self
def __next__(self):
if self.index == len(self.data):
raise StopIteration
value=self.data[self.index]
self.index += 1
return value
string=An_Iterator('abc')
next(string) #也可以寫作 string.__next__() 得到元素 'a'
next(string) # 'b'
next(string) # 'c'
next(string) # 拋出異常 StopIteration
在上面的代碼塊中,先創(chuàng)建了 An_Iterator 的類,內(nèi)部有個(gè)iter的方法,返回對象本身。當(dāng)訪問內(nèi)部的next方法時(shí),返回下一個(gè)元素。當(dāng)超出了范圍后,返回異常。
實(shí)際上滿足下面兩個(gè)條件的都可以稱為迭代器
(1)iter() 方法返回迭代器對象本身。
(2)next() 方法會(huì)返回下一個(gè)迭代器對象。如果數(shù)據(jù)沒有了,拋出 StopIteration 異常。
for i in string:
print(i)
一般不用next方法來返回,可以用 for 循環(huán)來遍歷循環(huán)。在 for 語句的幕后,其實(shí) python 先對對象調(diào)用了 iter 函數(shù)。這個(gè)函數(shù)返回了迭代器對象,這邊也就是返回了本身。返回的對象能夠使用 next 方法,調(diào)用下一個(gè)元素。
生成器(generator)
生成器是一種特殊的迭代器,也含有iter和next方法。
生成器的構(gòu)造
生成器可以通過2種方法進(jìn)行構(gòu)造。
(1) 將函數(shù)定義中的 return 變成 yield,即可定義一個(gè)生成器
每次語句執(zhí)行到 yield 時(shí),就跳出了定義的函數(shù)。下一次再執(zhí)行,則從上次中斷的地方繼續(xù)執(zhí)行。所以在迭代生成器時(shí),每一次執(zhí)行都可以保留上一次的狀態(tài),而不是像普通方法那樣,遇到 return 就返回結(jié)果,下一次執(zhí)行只能再次重復(fù)上一次的流程。
def squares(n=10):
'''返回1:n^2'''
for i in range(1,n+1):
yield i**2
a=squares(3) #此處并未執(zhí)行squares中的代碼,只是創(chuàng)建了一個(gè)生成器對象
next(a) #1
next(a) #4
next(a) #9
for element in a:
print(element,end=' ')
#1 4 9
再看下一個(gè)例子,更深入理解 yield 的用法。
def counter_generator(low, high):
while low <= high:
yield low
low += 1
for i in counter_generator(5,10):
print(i, end=' ')
# 5 6 7 8 9 10
在 while 循環(huán)中,當(dāng)它到達(dá) yield 語句時(shí),將返回 low 值,并退出生成器。在第二次下一次調(diào)用期間,發(fā)生器在之前中斷的地方恢復(fù),然后low+1。再繼續(xù)使用 while 循環(huán),并再次進(jìn)入 yield 語句。
之前提到生成器和迭代器都有iter和next方法,可以通過dir() 函數(shù)查看。
dir(string)
dir(a)
#能在輸出的列表中找到__iter__和__next__方法
生成器最大的好處就是能夠處理很大的數(shù)據(jù)而不占用很大內(nèi)存, 因?yàn)樗且贿呌?jì)算一邊生成元素的,而不是一下子把所有的元素都導(dǎo)入到內(nèi)存中,所以大大節(jié)省了內(nèi)存,理論上可以產(chǎn)生包含無限元素的生成器。
(2)生成器表達(dá)式
在列表表達(dá)式中,以中括號(hào)的形式來創(chuàng)建,生成器表達(dá)式只要把中括號(hào)替換成小括號(hào)即可。
gen=(x**2 for x in range(10))
for i in gen:
print(i)
生成器有一個(gè)問題就是不能重復(fù)使用,當(dāng)執(zhí)行完上面的代碼后,再執(zhí)行依次,不會(huì)有內(nèi)容輸出。
可迭代對象(Iterable)
可迭代對象有iter方法,能夠返回一個(gè)迭代器。
#還是上面這樣一個(gè)迭代器
class An_Iterator(object):
def __init__(self,data):
self.data=data
self.index=0
def __iter__(self):
return self
def __next__(self):
if self.index == len(self.data):
raise StopIteration
value=self.data[self.index]
self.index += 1
return value
class It(object):
def __init__(self,data):
self.data=data
def __iter__(self):
return An_Iterator(self.data)
b=It('abcd') #b是一個(gè)可迭代對象
dir(b) #輸出中,b包含了__iter__,而沒有__next__方法
上述的代碼中,定義了一個(gè) An_Iterator 的迭代器,定義了 It 類,這個(gè)類包含了iter方法,并且返回了迭代器An__Iterator,符合可迭代的對應(yīng),b是 It 的一個(gè)實(shí)例化, 所以b是可迭代對象。
在 python 的數(shù)據(jù)結(jié)構(gòu)中,字符串,tuple,list,dict,set均為可迭代對象,而文件對象是迭代器對象。
對可迭代對象,也可以通過for循環(huán)遍歷,但是不能使用next方法
因?yàn)?for 語句執(zhí)行過程中,先將對象使用iter方法,這時(shí)候就返回一個(gè)迭代器,而迭代器自然可以用next方法。
判斷迭代器和可迭代對象
from collections.abc import Iterable,Iterator
list_1=[1,2,3]
isinstance(list_1,Iterable) #True
isinstance(list_1,Iteration) #False
從上面的結(jié)果也可以看到,列表是可迭代對象,而不是迭代器。