詳解Python中迭代器和生成器的原理與使用
關(guān)于python中迭代器,生成器介紹的文章不算少數(shù),有些寫的也很透徹,但是更多的是碎片化的內(nèi)容。本來可迭代對象、迭代器、生成器概念就很繞,又加上過于碎片的內(nèi)容,更讓人摸不著頭腦。本篇嘗試用系統(tǒng)的介紹三者的概念和關(guān)系,希望能夠幫助需要的人。
1.可迭代對象、迭代器
1.1概念簡介
迭代:
首先看迭代的字面意思:
迭代的意思就是:迭代是一種行為,反復(fù)執(zhí)行的動作。在python中可以理解為反復(fù)取值的動作。
可迭代對象:顧名思義就是可以從里面迭代取值的對象,在python中容器類的數(shù)據(jù)結(jié)構(gòu)都是可迭代對象,如列表,字典,集合,元組等。
迭代器:類似于從可迭代對象中取值的一種工具,嚴(yán)謹(jǐn)?shù)恼f可以將可迭代對象中的值取出的對象。
1.2可迭代對象
在python中,容器類型的數(shù)據(jù)結(jié)構(gòu)都是可迭代對象,列舉如下:
- 列表
- 字典
- 元組
- 集合
- 字符串
>>> arr = ['圣僧','大圣','天蓬','卷簾'] >>> for i in arr: ... print(i) ... 圣僧 大圣 天蓬 卷簾 >>>
除了python自帶的數(shù)據(jù)結(jié)構(gòu)是可迭代對象之外,模塊里的方法、自定義的類也可能是可迭代對象。那么如何確認(rèn)一個對象是否為可迭代對象呢?有一個標(biāo)準(zhǔn),那就是可迭代對象都有方法__iter__
,凡是具有該方法的對象都是可迭代對象。
>>> dir(arr) ['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
1.3迭代器
常見迭代器是從可迭代對象創(chuàng)建而來。調(diào)用可迭代對象的__iter__
方法就可以為該可迭代對象創(chuàng)建其專屬迭代器。使用iter()
方法也可以創(chuàng)建迭代器,iter()
本質(zhì)上就是調(diào)用可迭代對象的__iter__
方法。
>>> arr = ['圣僧','大圣','天蓬','卷簾'] >>> arr_iter = iter(arr) >>> >>> for i in arr_iter: ... print(i) ... 圣僧 大圣 天蓬 卷簾 >>> >>> >>> arr_iter = iter(arr) >>> next(arr_iter) '圣僧' >>> next(arr_iter) '大圣' >>> next(arr_iter) '天蓬' >>> next(arr_iter) '卷簾' >>> next(arr_iter) Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration
可迭代對象只能通過for循環(huán)來遍歷,而迭代器除了可以通過for循環(huán)來遍歷,重要的是還可以通過next()
方法來迭代出元素。調(diào)用一次迭代出一個元素,直到所有元素都迭代完,拋出StopIteration
錯誤。這個過程就像象棋中沒有過河的小卒子——只能前進(jìn)不能后退,并且迭代完所有元素也無法再次遍歷。
簡單總結(jié)迭代器的特征:
- 可以使用
next()
方法迭代取值 - 迭代的過程只能向前不能后退
- 迭代器是一次性的,迭代完所有元素就無法再次遍歷,需要再次遍歷只有新建迭代器
迭代器對象在python中很常見,比如打開的文件就是一個迭代器、map,filter,reduce等高階函數(shù)的返回也是迭代器。迭代器對象擁有兩個方法:__iter__
和__next__
。next()
方法能迭代出元素就是調(diào)用__next__
來實現(xiàn)的。
>>> dir(arr_iter) ['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__length_hint__', '__lt__', '__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__']
1.4區(qū)分可迭代對象和迭代器
如何區(qū)分可迭代對象和迭代器呢?在python的數(shù)據(jù)類型增強(qiáng)模塊collections
中有可迭代對象和迭代器數(shù)據(jù)類型,通過isinstance
類型比較即可區(qū)分出兩者。
>>> from collections import Iterable, Iterator >>> arr = [1,2,3,4] >>> isinstance(arr, Iterable) True >>> isinstance(arr, Iterator) False >>> >>> >>> arr_iter = iter(arr) >>> isinstance(arr_iter, Iterable) True >>> isinstance(arr_iter, Iterator) True >>>
arr:可迭代對象。是可迭代對象類型,不是迭代器類型
arr_iter:迭代器。既是可迭代對象類型,又是迭代器類型
1.5可迭代對象和迭代器的關(guān)系
從迭代器的創(chuàng)建就能大致看出。可迭代對象就是一個集合,而迭代器就是為這個集合創(chuàng)建的迭代方法。迭代器迭代時是直接從可迭代對象集合里取值??梢杂萌缦履P蛠砝斫鈨烧咧g的關(guān)系:
>>> arr = [1,2,3,4] >>> iter_arr = iter(arr) >>> >>> arr.append(100) >>> arr.append(200) >>> arr.append(300) >>> >>> for i in iter_arr: ... print(i) ... 1 2 3 4 100 200 300 >>>
可以看到這里的流程是:
- 先創(chuàng)建可迭代對象arr
- 然后從arr創(chuàng)建的arr_iter迭代器
- 再向arr列表追加元素
- 最后迭代出來的元素包括后追加的元素。
可以說明迭代器并不是copy了可迭代對象的元素,而是引用了可迭代對象的元素。在迭代取值時直接使用了可迭代對象的元素。
1.6可迭代對象和迭代器的工作機(jī)制
首先整理一下兩者的方法
可迭代對象: 對象中有__iter__
方法
迭代器:對象中有__iter__
和 __next__
方法
在迭代器的創(chuàng)建時提到過__iter__
方法是返回一個迭代器,__next__
是從元素中取值。所以,關(guān)于兩者方法的功能:
可迭代對象:
__iter__
方法的作用是返回一個迭代器
迭代器:
__iter__
方法的作用是返回一個迭代器,就是自己。
__next__
方法的作用是返回集合中下一個元素
可迭代對象是一個元素集合,本身沒有自帶取值的方法,可迭代對象就像老話說的茶壺里的餃子,有貨倒不出。
>>> arr = [1,2,3,4] >>> >>> next(arr) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'list' object is not an iterator
既然餃子倒不出來,又想吃怎么辦?那就得找筷子一樣的工具來夾出來對吧。而迭代器就是給用來給可迭代對象取值的工具。
給可迭代對象arr創(chuàng)建的迭代器arr_iter,可以通過next取值,將arr中值全部迭代出來,直到?jīng)]有元素拋出異常StopIteration
>>> arr_iter = iter(arr) >>> >>> next(arr_iter) 1 >>> next(arr_iter) 2 >>> next(arr_iter) 3 >>> next(arr_iter) 4 >>> next(arr_iter) Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration >>>
for 循環(huán)本質(zhì)
>>> arr = [1,2,3] >>> for i in arr: ... print(i) ... 1 2 3
以上通過for循環(huán)遍歷出arr中所有值。我們知道列表arr是可迭代對象,本身無法取值,for循環(huán)如何迭代出所有元素呢?
for循環(huán)的本質(zhì)就是給arr創(chuàng)建一個迭代器,然后不斷調(diào)用next()方法取出元素,復(fù)制給變量i,直到?jīng)]有元素拋出捕獲StopIteration的異常,退出循環(huán)。可以通過模擬for循環(huán)更直觀的說明:
arr = [1,2,3] # 給arr生成一個迭代器 arr_iter = iter(arr) while True: try: # 不斷調(diào)用迭代器next方法,并捕獲異常,然后退出 print(next(arr_iter)) except StopIteration: break >> 1 2 3
到這里大概就講完了可迭代對象和迭代器的工作機(jī)制,簡單總結(jié):
可迭代對象: 保存元素,但自身無法取值??梢哉{(diào)用自己的__iter__
方法創(chuàng)建一個專屬迭代器來取值。
迭代器:擁有__next__
方法,可以從指向的可迭代對象中取值。只能遍歷一遍,并且只能前進(jìn)不能后退。
1.7自己動手創(chuàng)建可迭代對象和迭代器
榴蓮好不好吃,只有嘗一嘗才知道。迭代器好不好理解,動手實現(xiàn)一次就清楚。下面自定義可迭代對象和迭代器。
如果自定義一個可迭代對象,那么需要實現(xiàn)__iter__
方法;
如果要自定義一個迭代器,那么就需要實現(xiàn)__iter__
和__next__
方法。
可迭代對象:實現(xiàn)__iter__
方法,功能是調(diào)用該方法返回迭代器
迭代器:實現(xiàn)__iter__
,功能是返回迭代器,也就是自身;實現(xiàn)__next__
,功能是迭代取值直到拋出異常。
from collections import Iterable, Iterator # 可迭代對象 class MyArr(): def __init__(self): self.elements = [1,2,3] # 返回一個迭代器,并將自己元素的引用傳遞給迭代器 def __iter__(self): return MyArrIterator(self.elements) # 迭代器 class MyArrIterator(): def __init__(self, elements): self.index = 0 self.elements = elements # 返回self,self就是實例化的對象,也就是調(diào)用者自己。 def __iter__(self): return self # 實現(xiàn)取值 def __next__(self): # 迭代完所有元素拋出異常 if self.index >= len(self.elements): raise StopIteration value = self.elements[self.index] self.index += 1 return value arr = MyArr() print(f'arr 是可迭代對象:{isinstance(arr, Iterable)}') print(f'arr 是迭代器:{isinstance(arr, Iterator)}') # 返回了迭代器 arr_iter = arr.__iter__() print(f'arr_iter 是可迭代對象:{isinstance(arr_iter, Iterable)}') print(f'arr_iter 是迭代器:{isinstance(arr_iter, Iterator)}') print(next(arr_iter)) print(next(arr_iter)) print(next(arr_iter)) print(next(arr_iter))
結(jié)果:
arr 是可迭代對象:True
arr 是迭代器:False
arr_iter 是可迭代對象:True
arr_iter 是迭代器:True
1
2
3
Traceback (most recent call last):
File "myarr.py", line 40, in <module>
print(next(arr_iter))
File "myarr.py", line 23, in __next__
raise StopIteration
StopIteration
從這個列子就能清晰的認(rèn)識可迭代對象的迭代器的實現(xiàn)??傻鷮ο蟮?code>__iter__方法返回值就是一個實例化的迭代器的對象。這個迭代器的對象保存了可迭代對象的元素的引用,也實現(xiàn)了取值的方法,所以可以通過next()
方法取值。這是一個值得細(xì)品的代碼,比如說有幾個問題可以留給讀者思考:
- 為什么next()只能前進(jìn)不能后退
- 為什么迭代器只能遍歷一次就失效
- 如果for循環(huán)的目標(biāo)是迭代器,工作機(jī)制是怎樣
1.8迭代器的優(yōu)勢
設(shè)計模式之迭代模式
迭代器的優(yōu)勢是:提供了一種通用不依賴索引的迭代取值方式
迭代器的設(shè)計來源于設(shè)計模式之迭代模式。迭代模式的思想是:提供一種方法順序地訪問一個容器中的元素,而又不需要暴露該對象的內(nèi)部細(xì)節(jié)。
迭代模式具體到python的迭代器中就是能夠將遍歷序列的操作和序列底層相分離,提供一種通用的方法去遍歷元素。
如列表、字典、集合、元組、字符串。這些數(shù)據(jù)結(jié)構(gòu)的底層數(shù)據(jù)模型都不一樣,但是同樣都可以使用for循環(huán)來遍歷。正是因為每一種數(shù)據(jù)結(jié)構(gòu)都可以生成迭代器,都可以通過next()方法迭代,所以在使用的時候不需要關(guān)心元素的在底層如何保存,不需要考慮內(nèi)部細(xì)節(jié)。
同樣如果是自定的數(shù)據(jù)類型,即使是內(nèi)部實現(xiàn)比較復(fù)雜,只需要實現(xiàn)迭代器,也就不需要關(guān)心復(fù)雜的結(jié)構(gòu),使用通用的next方法即可遍歷元素。
復(fù)雜數(shù)據(jù)結(jié)構(gòu)的通用取值實現(xiàn)
比如我們構(gòu)造一個復(fù)雜的數(shù)據(jù)結(jié)構(gòu):{(x,x):value}
,一個字典,key是元組,value是數(shù)字。按照迭代的設(shè)計模式,實現(xiàn)通用取值方法。
例子實現(xiàn):
class MyArrIterator(): def __init__(self): self.index = 1 self.elements = {(1,1):100, (2,2):200, (3,3):300} def __iter__(self): return self def __next__(self): if self.index > len(self.elements): raise StopIteration value = self.elements[(self.index, self.index)] self.index += 1 return value arr_iter = MyArrIterator() print(next(arr_iter)) print(next(arr_iter)) print(next(arr_iter)) print(next(arr_iter))
執(zhí)行結(jié)果:
100
200
300
Traceback (most recent call last):
File "iter_two.py", line 22, in <module>
print(next(arr_iter))
File "iter_two.py", line 12, in __next__
raise StopIteration
StopIteration
只要實現(xiàn)了__next__
方法就可以通過next()取值,不管數(shù)據(jù)結(jié)構(gòu)多么復(fù)雜,__next__
屏蔽了底層細(xì)節(jié)。這種設(shè)計思想是一個比較常見的思想,比如驅(qū)動設(shè)計,第三方平臺介入設(shè)計等都是屏蔽差異,提供一個統(tǒng)一的調(diào)用方法。
1.9迭代器的缺點和誤區(qū)
缺點
在上面的介紹中也提到了迭代器的缺點,集中說一下:
- 取值不夠靈活。next方法只能往后取值,不能往前。取值不如按照索引的方式靈活,不能取指定的某一個值
- 無法預(yù)測迭代器的長度。迭代器通過next()方法取值,并不能提前知道要迭代出的個數(shù)
- 用完一次就失效
誤區(qū)
迭代器的優(yōu)勢和缺點已經(jīng)說的清晰了,現(xiàn)在討論一個普遍對迭代器的一個誤區(qū):迭代器是不能節(jié)省內(nèi)存的給這句話加一個前提:這里的迭代器是指普通的迭代器,而非生成器,因為生成器也是一種特殊的迭代器。
這可能是一個認(rèn)識的誤區(qū),認(rèn)為創(chuàng)建一個功能相同的可迭代對象和迭代器,迭代器的內(nèi)存占用小于可迭代對象。例如:
>>> arr = [1,2,3,4] >>> arr_iter = iter([1,2,3,4]) >>> >>> arr.__sizeof__() 72 >>> arr_iter.__sizeof__() 32
咋一看確實是迭代器占用的內(nèi)存小于可迭代對象,可仔細(xì)想一下迭代器的實現(xiàn),它是引用了可迭代對象的元素,也就是說創(chuàng)建迭代器arr_iter同時也創(chuàng)建了一個列表[1,2,3,4],迭代器只是保存了列表的引用,所以迭代器的arr_iter實際的內(nèi)存是[1,2,3,4] + 32= 72 + 32 = 104字節(jié)。
arr_iter
本質(zhì)上是一個類的對象,因為python變量是保存地址的特性,所以對象的的地址大小都是32字節(jié)。
后面有專門關(guān)于迭代器和生成器占用內(nèi)存的分析,能夠用數(shù)字來證明這個觀點。
1.10python自帶的迭代器工具itertools
迭代器在python占有重要的位置,所以python內(nèi)置了迭代器功能模塊itertools
。itertools中所有的方法都是迭代器,可以使用next()取值。方法主要可以分為三類,分別是無限迭代器,有限迭代器,組合迭代器
無限迭代器count():創(chuàng)建一個無限的迭代器,類似于無限長度的列表,可以從中取值
有限迭代器chain():可以把多個可迭代對象組合起來,形成一個更大的迭代器
組合迭代器product():得到的是可迭代對象的笛卡兒積
關(guān)于更多itertools的使用可參考:Python中itertools模塊的使用教程詳解
2.生成器
生成器是一種特殊的迭代器,它既具有迭代器的功能:能夠通過next方法迭代出元素,又有自己的特殊之處:節(jié)省內(nèi)存。
2.1生成器的創(chuàng)建方法
生成器有兩種創(chuàng)建方法,分別是:
()
語法,將列表生成式的[]
換成()
就可以創(chuàng)建生成器- 使用
yield
關(guān)鍵字將普通函數(shù)變成生成器函數(shù)
()語法
>>> gen = (i for i in range(3)) >>> type(gen) <class 'generator'> >>> from collections import Iterable,Iterator >>> >>> isinstance(gen, Iterable) True >>> isinstance(gen, Iterator) True >>>
>>> next(gen) 0 >>> next(gen) 1 >>> next(gen) 2 >>> next(gen) Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration >>>
可以看到生成器符合迭代器的特征。
yield 關(guān)鍵字
yield
是python的關(guān)鍵字,在函數(shù)中使用yield就能將普通函數(shù)變成生成器。函數(shù)中return是返回標(biāo)識,代碼執(zhí)行到return就退出了。而代碼執(zhí)行到y(tǒng)ield時也會返回yield后面的變量,但是程序只是暫停在當(dāng)前位置,當(dāng)再次運(yùn)行程序時會從yield之后的部分開始執(zhí)行。
from collections import Iterator,Iterable def fun(): a = 1 yield a b = 100 yield b gen_fun = fun() print(f'是可迭代對象:{isinstance(gen_fun, Iterable)}') print(f'是迭代器:{isinstance(gen_fun, Iterator)}') print(next(gen_fun)) print(next(gen_fun)) print(next(gen_fun))
執(zhí)行結(jié)果:
是可迭代對象:True
是迭代器:True
1
100
Traceback (most recent call last):
File "gen_fun.py", line 17, in <module>
print(next(gen_fun))
StopIteration
執(zhí)行第一個next()時,程序通過yield a返回了1,執(zhí)行流程就暫停在這里。
執(zhí)行第二個next()時,程序從上次暫停的地方開始運(yùn)行,然后通過yield b返回了100,最后退出,程序結(jié)束。
yield的魔力就是能夠記住執(zhí)行位置,并且能夠從執(zhí)行位置再次執(zhí)行下去。
2.2生成器方法
生成器既然是一種特殊的迭代器,那么是否具有迭代器對象的兩個方法呢?查看兩種生成器擁有的方法。
gen
>>> dir(gen) ['__class__', '__del__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__lt__', '__name__', '__ne__', '__new__', '__next__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'close', 'gi_code', 'gi_frame', 'gi_running', 'gi_yieldfrom', 'send', 'throw']
gen_fun
['__class__', '__del__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__lt__', '__name__', '__ne__', '__new__', '__next__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'close', 'gi_code', 'gi_frame', 'gi_running', 'gi_yieldfrom', 'send', 'throw']
兩種生成器都有用迭代器的__iter__
和__next__
方法。
生成器是特殊的迭代器,想要區(qū)分出生成器和迭代器就不能使用collections
的Iterator
了。可以使用isgenerator
方法:
>>> from inspect import isgenerator >>> arr_gen = (i for i in range(10)) >>> isgenerator(arr_gen) True >>> >>> arr = [i for i in range(10)] >>> isgenerator(arr) False >>>
2.3生成器的優(yōu)勢
生成器是一種特殊的迭代器,它的特殊之處就是它的優(yōu)勢:節(jié)省內(nèi)存。從名字就可以看出,生成器,通過生成的方法來支持迭代取值。
節(jié)省內(nèi)存原理:以遍歷列表為例,列表元素按照某種算法推算出來,就可以在循環(huán)的過程中不斷推算出后續(xù)的元素,這樣就不必創(chuàng)建完整的列表,從而節(jié)省大量的空間。
以實現(xiàn)同樣的的功能為例,迭代出集合中的元素。集合為:[1,2,3,4]
迭代器的做法:
- 首先生成一個可迭代對象,列表[1,2,3,4]
- 然后創(chuàng)建迭代器,從可迭代對象中通過next()取值
arr = [1,2,3,4] arr_iter = iter(arr) next(arr_iter) next(arr_iter) next(arr_iter) next(arr_iter)
生成器的做法:
- 創(chuàng)建一個生成器函數(shù)
- 通過next取值
def fun(): n = 1 while n <= 4: yield n n += 1 gen_fun = fun() print(next(gen_fun)) print(next(gen_fun)) print(next(gen_fun)) print(next(gen_fun))
比較這兩種方法,迭代器需要創(chuàng)建一個列表來完成迭代,而生成器只需要一個數(shù)字就可以完成迭代。在數(shù)據(jù)量小的情況下還不能體現(xiàn)這個優(yōu)勢,當(dāng)數(shù)據(jù)量巨大時這個優(yōu)勢能展現(xiàn)的淋漓盡致。比如同樣生成10w個數(shù)字,迭代器需要10w個元素的列表,而生成器只需要一個元素。當(dāng)然就能節(jié)省內(nèi)存。
生成器是一種以時間換空間的做法,迭代器是從已經(jīng)在內(nèi)存中創(chuàng)建好的集合中取值,所以消耗內(nèi)存空間,而生成器只保存一個值,取一次值就計算一次,消耗cpu但節(jié)省內(nèi)存空間。
2.4生成器應(yīng)用場景
- 數(shù)據(jù)的數(shù)據(jù)規(guī)模巨大,內(nèi)存消耗嚴(yán)重
- 數(shù)列有規(guī)律,但是依靠列表推導(dǎo)式描述不出來
- 協(xié)程。生成器和協(xié)程有著千絲萬縷的聯(lián)系
3.生成器節(jié)省內(nèi)存、迭代器不節(jié)省內(nèi)存
實踐是檢驗真理的唯一標(biāo)準(zhǔn),通過記錄內(nèi)存的變化來檢測迭代器和生成器哪個能夠節(jié)省內(nèi)存。
環(huán)境:
系統(tǒng):Linux deepin 20.2.1
內(nèi)存:8G
python版本: 3.7.3
內(nèi)存監(jiān)控工具: free -b 以字節(jié)為單位的內(nèi)存展示
方法:生成100萬規(guī)模的列表,從0到100w,對比生成數(shù)據(jù)前后的內(nèi)存變化
3.1可迭代對象
>>> arr = [i for i in range(1000000)] >>> >>> arr.__sizeof__() 8697440 >>>
第一次free -b
在生成列表之前;第二次在生成列表之后。下同
ljk@work:~$ free -b total used free shared buff/cache available Mem: 7978999808 1424216064 2386350080 362094592 4168433664 5884121088 Swap: 0 0 0 ljk@work:~$ free -b total used free shared buff/cache available Mem: 7978999808 1464410112 2352287744 355803136 4162301952 5850210304 Swap: 0 0 0
現(xiàn)象:內(nèi)存增加:從1424216064
字節(jié)增加1464410112字
節(jié),增加 38.33
MB
3.2迭代器
>>> a = iter([i for i in range(1000000)]) >>> >>> a.__sizeof__() 32
ljk@work:~$ free -b total used free shared buff/cache available Mem: 7978999808 1430233088 2385924096 355160064 4162842624 5885038592 Swap: 0 0 0 ljk@work:~$ free -b total used free shared buff/cache available Mem: 7978999808 1469304832 2346835968 355160064 4162859008 5845966848 Swap: 0 0 0
現(xiàn)象:內(nèi)存增加:從1430233088
字節(jié)增加1469304832
節(jié),增加 37.26
MB
3.3生成器
>>> arr = (i for i in range(1000000)) >>> >>> arr.__sizeof__() 96 >>>
ljk@work:~$ free -b total used free shared buff/cache available Mem: 7978999808 1433968640 2373222400 362868736 4171808768 5873594368 Swap: 0 0 0 ljk@work:~$ free -b total used free shared buff/cache available Mem: 7978999808 1434963968 2378940416 356118528 4165095424 5879349248 Swap: 0 0 0
現(xiàn)象:內(nèi)存增加:從1433968640
字節(jié)增加1434963968
節(jié),增加 0.9492
MB
3.4小結(jié)
- | 系統(tǒng)內(nèi)存 | 變量內(nèi)存 |
---|---|---|
可迭代對象 | 38.33MB | 8.29MB |
迭代器 | 37.26MB | 32k |
生成器 | 0.9492MB | 96k |
以上結(jié)論經(jīng)過多次實現(xiàn),基本保存變量一致。從數(shù)據(jù)結(jié)果來看迭代器不能節(jié)省內(nèi)存,生成器可以節(jié)省內(nèi)存。生成100w規(guī)模的數(shù)據(jù),迭代器的內(nèi)存消耗是生成器的40倍左右,結(jié)果存在一定誤差。
4.總結(jié)
可迭代對象:
屬性:一種容器對象
特點:能夠保存元素集合,自己無法實現(xiàn)迭代取值,在外界的幫助下可以迭代取值
特征:有__iter__
方法
迭代器:
屬性:一種工具對象
特點:可以實現(xiàn)迭代取值,取值的來源是可迭代對象保存的集合
特征:有__iter__
和__next__
方法
優(yōu)點:實現(xiàn)通用的迭代方法
生成器:
屬性:一種函數(shù)對象
特點:可以實現(xiàn)迭代取值,只保存一個值,通過計算返回迭代的下一個值。以計算換內(nèi)存。
特征:有__iter__
和__next__
方法
優(yōu)點:擁有迭代器特點同時能夠節(jié)省內(nèi)存
關(guān)于可迭代對象、迭代器、生成器的內(nèi)容講的比較多,不知道讀者是不是已經(jīng)云里霧里了?出道題檢驗一下:在文中使用的例子里西游記第一天團(tuán)的人物名字是以誰的視角來稱呼的?
以上就是詳解Python中迭代器和生成器的原理與使用的詳細(xì)內(nèi)容,更多關(guān)于Python迭代器 生成器的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
pycharm 將django中多個app放到同個文件夾apps的處理方法
今天小編就為大家分享一篇pycharm 將django中多個app放到同個文件夾apps的處理方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-05-05python中django框架通過正則搜索頁面上email地址的方法
這篇文章主要介紹了python中django框架通過正則搜索頁面上email地址的方法,涉及django框架及正則表達(dá)式的使用技巧,需要的朋友可以參考下2015-03-03關(guān)于命令行執(zhí)行Python腳本的傳參方式
這篇文章主要介紹了關(guān)于命令行執(zhí)行Python腳本的傳參方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-09-09python實現(xiàn)測試工具(一)——命令行發(fā)送get請求
這篇文章主要介紹了python如何實現(xiàn)命令行發(fā)送get請求,幫助大家更好的利用python進(jìn)行測試工作,感興趣的朋友可以了解下2020-10-10ubuntu?20.04系統(tǒng)下如何切換gcc/g++/python的版本
這篇文章主要給大家介紹了關(guān)于ubuntu?20.04系統(tǒng)下如何切換gcc/g++/python版本的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用ubuntu具有一定的參考借鑒價值,需要的朋友可以參考下2023-12-12高性能web服務(wù)器框架Tornado簡單實現(xiàn)restful接口及開發(fā)實例
Tornado和現(xiàn)在的主流Web服務(wù)器框架(包括大多數(shù)Python的框架)有著明顯的區(qū)別:它是非阻塞式服務(wù)器,而且速度相當(dāng)快。得利于其 非阻塞的方式和對epoll的運(yùn)用,Tornado每秒可以處理數(shù)以千計的連接,這意味著對于實時Web服務(wù)來說,Tornado是一個理想的Web框架。2014-07-07