Python 迭代器(Iterator)
?
目錄
一、什么是迭代器
二、可迭代的對(duì)象
三、創(chuàng)建一個(gè)迭代器
四、迭代器的應(yīng)用
五、小結(jié)
一、什么是迭代器
迭代是python中訪問集合元素的一種非常強(qiáng)大的一種方式。迭代器是一個(gè)可以記住遍歷位置的對(duì)象,因此不會(huì)像列表那樣一次性全部生成,而是可以等到用的時(shí)候才生成,因此節(jié)省了大量的內(nèi)存資源。迭代器對(duì)象從集合中的第一個(gè)元素開始訪問,直到所有的元素被訪問完。迭代器有兩個(gè)方法:iter()和next()方法。
二、可迭代的對(duì)象
類似于list、tuple、str 等類型的數(shù)據(jù)可以使用for …… in…… 的循環(huán)遍歷語法從其中依次拿到數(shù)據(jù)并進(jìn)行使用,我們把這個(gè)過程稱為遍歷,也稱迭代。python中可迭代的對(duì)象有list(列表)、tuple(元組)、dirt(字典)、str(字符串)set等。
mylist = [1,2,3,4,5] mydirt = {"name":"張三","sex":"男","age":18}mytuple = (1,2,3,4,5) myset = {1,2,3,3,4}for i in mylist:print(i)for i in mytuple:print(i)for i in myset:print(i)for i,j in mydirt.items():print("%s:%s" % (i,j))除此之外,也可以創(chuàng)建一個(gè)可迭代的對(duì)象:只要此對(duì)象含有__iter__方法,那么它就是一個(gè)可迭代的對(duì)象,如下面的例子:其中定義了一個(gè)__iter__方法,我們通過isinstance()函數(shù)以及Iterable來判斷由Classmate類創(chuàng)建的class1對(duì)象是否是可迭代的對(duì)象:若class1是一個(gè)Iterable(可迭代對(duì)象)則結(jié)果返回為True;否則,結(jié)果為False。
from collections import Iterableclass Classmate(object):"""定義一個(gè)同學(xué)類"""def __init__(self):self.name = list()self.name_num = 0def add(self,name):self.name.append(name)def __iter__(self):passclass1 = Classmate() class1.add("張三") class1.add("李四") class1.add("王五")print("判斷是否是可迭代的對(duì)象:", isinstance(class1,Iterable))運(yùn)行結(jié)果如下:
三、創(chuàng)建一個(gè)迭代器
1.一個(gè)類(對(duì)象)只要含有“__iter__”、"__next__"兩個(gè)方法,就將其稱為迭代器。__iter__方法返回一個(gè)特殊的迭代器對(duì)象,而這個(gè)迭代器對(duì)象自動(dòng)實(shí)現(xiàn)了_next__方法,并返回一個(gè)值,最后通過拋出異常StopIteration來結(jié)束迭代。我們來給上一個(gè)例子增加__next__方法:
from collections import Iterable from collections import Iteratorclass Classmate(object):"""定義一個(gè)同學(xué)類"""def __init__(self):self.name = list()self.name_num = 0def add(self,name):self.name.append(name)def __iter__(self):passdef __next__(self):passclass1 = Classmate() class1.add("張三") class1.add("李四") class1.add("王五")print("判斷是否是可迭代的對(duì)象:", isinstance(class1,Iterable))print("判斷是否是迭代器:", isinstance(class1,Iterator))還是通過isinstance()函數(shù)來判斷,如果class1是一個(gè)迭代器(Iterator),則結(jié)果返回True;否則返回False。運(yùn)行結(jié)果如下:
2.只是名義上的 可迭代對(duì)象/迭代器 還不夠,具有相應(yīng)的功能才算是完整。首先,對(duì)于__iter__方法,它需要具有一個(gè)可以返回一個(gè)迭代器對(duì)象的功能(這個(gè)對(duì)象可以是自己(前提是本身就是一個(gè)迭代器),也可以是其它迭代器);對(duì)于__next__方法,它需要標(biāo)記并返回下一個(gè)迭代器對(duì)象。代碼如下(為防止迭代速度過快,我們添加sleep來控制速度):
from collections import Iterable from collections import Iterator import timeclass Classmate(object):"""定義一個(gè)同學(xué)類"""def __init__(self):self.name = list()self.name_num = 0def add(self,name):self.name.append(name)def __iter__(self):return self # 返回本身def __next__(self):# 記憶性返回?cái)?shù)據(jù)if self.name_num < len(self.name):ret = self.name[self.name_num]self.name_num += 1return retclass1 = Classmate() class1.add("張三") class1.add("李四") class1.add("王五")print("判斷是否是可迭代的對(duì)象:", isinstance(class1,Iterable))print("判斷是否是迭代器:", isinstance(class1,Iterator))for name in class1:print(name)time.sleep(1)運(yùn)行結(jié)果如下:
可以發(fā)現(xiàn),當(dāng)需要讀取的內(nèi)容讀取完之后不會(huì)停止,而是無限循環(huán)的繼續(xù)返回空值。因此我們可以為其添加拋出異常(StopIteration,python中用于標(biāo)標(biāo)識(shí)迭代完成,防止出現(xiàn)無限循環(huán)的情況)的程序,當(dāng)需要的數(shù)據(jù)讀取完成時(shí)就拋出異常,從而結(jié)束:
from collections import Iterable from collections import Iterator import timeclass Classmate(object):"""定義一個(gè)同學(xué)類"""def __init__(self):self.name = list()self.name_num = 0def add(self,name):self.name.append(name)def __iter__(self):return self # 返回本身def __next__(self):if self.name_num < len(self.name):ret = self.name[self.name_num]self.name_num += 1return ret# 拋出異常,當(dāng)循環(huán)后自動(dòng)結(jié)束else:raise StopIterationclass1 = Classmate() class1.add("張三") class1.add("李四") class1.add("王五")print("判斷是否是可迭代的對(duì)象:", isinstance(class1,Iterable))print("判斷是否是迭代器:", isinstance(class1,Iterator))for name in class1:print(name)time.sleep(1)運(yùn)行結(jié)果如下:
四、迭代器的應(yīng)用
迭代器最核心的功能就是可以通過__next__方法的調(diào)用來返回下一個(gè)值。而這個(gè)值不是從已有的數(shù)據(jù)中讀取的,而是通過程序按照一定的規(guī)則生成的。這也就意味著我們可以不再依賴一個(gè)現(xiàn)存的數(shù)據(jù)集合來存放數(shù)據(jù),而是邊用邊生成,這樣的好處就是可以節(jié)省大量的內(nèi)存空間。我們通過對(duì)比以下問題的兩個(gè)解決方法,來進(jìn)一的理解其應(yīng)用優(yōu)勢(shì):
問題:試編寫一個(gè)程序?qū)崿F(xiàn)斐波那契數(shù)列(0,1,1,2,3,5,8,13,21……后一項(xiàng)總是等于前兩項(xiàng)的和):
方法一:
a = 0 b = 1 myFibonacci = list()nums = int(input("請(qǐng)輸入需要生成Fibonacci數(shù)列項(xiàng)的個(gè)數(shù):"))i = 0 while i < nums:myFabonacci.append(a)a,b = b,a+bi += 1for num in nums:print(num)方法二:
class Fibonacci(object):"""斐波那契數(shù)列得迭代器"""def __init__(self,nums):self.nums = nums # 傳入?yún)?shù),生成斐波那契數(shù)列的個(gè)數(shù)self.a = 0 self.b = 1self.i =0 # 用于記憶生成的個(gè)數(shù)def __iter__(self):return selfdef __next__(self):ret = self.a # 記憶第一個(gè)數(shù)if self.i < self.nums:self.a, self.b = self.b,self.a +self.bself.i += 1return retelse:raise StopIteration # 停止迭代nums = int(input("請(qǐng)輸入需要生成Fibonacci數(shù)列項(xiàng)的個(gè)數(shù):")) fobo = Fibonacci(nums)for num in fobo:print(num)假如我們需要生成10個(gè)Fibonacci數(shù)列的項(xiàng),兩者的運(yùn)行結(jié)果相同,如下:
雖然結(jié)果相同,但實(shí)際效果卻相差巨大。來看一下方法一,他是通過while循環(huán)立即生成一個(gè)列表用來存放數(shù)據(jù),接著再從已有的數(shù)據(jù)中讀取所需數(shù)據(jù),而這需要占用一定的內(nèi)存空間;再來看一下方法二,它并沒有用到列表,而是返回一個(gè)迭代器,在需要的時(shí)候生成相關(guān)數(shù)據(jù)。縱觀以上兩種方法,在生成個(gè)數(shù)較小時(shí),兩者相差不大,但當(dāng)生成個(gè)數(shù)是10萬,100萬,1000萬呢?前者需要消耗大量的內(nèi)存資源,而后者僅需占用一點(diǎn)內(nèi)存即可。這也是python2中range()函數(shù)和python3中range()函數(shù)的不同點(diǎn),python3的range()函數(shù)采用了迭代器的方式,不再依賴于現(xiàn)有的數(shù)據(jù)集合,也就相當(dāng)于python2中的xrange()函數(shù)。
五、小結(jié):
1.可迭代對(duì)象不一定是迭代器。
2.迭代器一定是可迭代對(duì)象。
3.容器類型(list tuple dict str set )是可迭代對(duì)象但不是迭代器。
?
總結(jié)
以上是生活随笔為你收集整理的Python 迭代器(Iterator)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SAP 成本核算流程
- 下一篇: python迭代器转list