欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解Python中迭代器和生成器的原理與使用

 更新時間:2022年05月09日 11:01:42   作者:金色旭光  
關(guān)于python中迭代器,生成器介紹的文章不算少數(shù),有些寫的也很透徹,但是更多的是碎片化的內(nèi)容。本篇嘗試用系統(tǒng)的介紹三者的概念和關(guān)系,需要的可以參考一下

關(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ū)分出生成器和迭代器就不能使用collectionsIterator了。可以使用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.33MB8.29MB
迭代器37.26MB32k
生成器0.9492MB96k

以上結(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)文章

最新評論