迭代器(iterator)

Date: 2019-05-23

Author: Sun

為何要引入迭代器?

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

? 我們是否可以在循環的過程中不斷推算出后續的元素呢?這樣就不必創建完整的list,從而節省大量的空間,在Python中,這種一邊循環一邊計算的機制,稱為生成器:generator

1.1 什么迭代器呢?

? 在Python中如果一個對象有__iter__( )方法和__next__( )方法,則稱這個對象是迭代器(Iterator);其中__iter__( )方法是讓對象可以用for ... in循環遍歷,next( )方法是讓對象可以通過next(實例名)訪問下一個元素。

? 注意:這兩個方法必須同時具備,才能稱之為迭代器。

? 迭代器是在python2.2中被加入的,它為類序列對象提供了一個類序列的接口。有了迭代器可以迭代一個不是序列的對象,因為他表現出了序列的行為。

? 迭代器的實質是實現了next()方法的對象,常見的元組、列表、字典都是迭代器。

迭代器中重點關注兩種方法:

__iter__方法:返回迭代器自身。可以通過python內建函數iter()調用。

__next__方法:當next方法被調用的時候,迭代器會返回它的下一個值,如果next方法被調用,但迭代器沒有只可以返回,就會引發一個StopIteration異常。該方法可以通過 python 內建函數next()調用。

舉例

內建函數iter()可以從可迭代對象中獲得迭代器。

>>> it = iter([1,2,3])
>>> next(it)
1
>>> next(it)
2
>>> next(it)
3
>>> next(it)
Traceback (most recent call last):File "<stdin>", line 1, in <module>
StopIteration
>>> 

? 迭代器是一個帶狀態的對象,他能在你調用next()方法的時候返回容器中的下一個值,任何實現了__iter____next__()(python2中實現next())方法的對象都是迭代器。

? __iter__返回迭代器自身,__next__返回容器中的下一個值,如果容器中沒有更多元素了,則拋出StopIteration異常,至于它們到底是如何實現的這并不重要。

? 迭代器就是實現了工廠模式的對象,它在你每次你詢問要下一個值的時候給你返回。

?

1.2 如何創建一個迭代器

要創建一個迭代器有2種方法,其中前兩種分別是:

  1. 為容器對象添加 __iter__()__next__() 方法(Python 2.7 中是 next());__iter__() 返回迭代器對象本身 self__next__() 則返回每次調用 next() 或迭代時的元素;
  2. 內置函數 iter() 將可迭代對象轉化為迭代器

案例:創建迭代器容器

# Create iterator Object
class Container:def __init__(self, start=0, end=0):self.start = startself.end = enddef __iter__(self):print("I made this iterator!")return selfdef __next__(self):print("Calling __next__ method!")if self.start < self.end:i = self.startself.start += 1return ielse:raise StopIteration()c = Container(0, 5)
for i in c:print(i)

? 對于迭代器對象,使用for循環遍歷整個數組其實是個語法糖,他的內部實現還是通過調用對象的__next__()方法。

? 創建迭代器對象的好處是當序列長度很大時,可以減少內存消耗,因為每次只需要記錄一個值即刻(經常看到人們介紹 Python 2.7 的 range 函數時,建議當長度太大時用 xrange 更快,在 Python 3.5 中已經去除了 xrange 只有一個類似迭代器一樣的 range)。

1.3 斐波那契數列應用舉例

我們如果采用正常的斐波那契數列求值過程如下:

def fibs(n):if n == 0 or n == 1:return 1else:return fibs(n-1) + fibs(n-2)print([fibs(i) for i in range(10)])

自定義一個迭代器, 實現斐波那契數列

class Fib2(object):def __init__(self, n):self.a = 0self.b = 1self.n = nself.count = 0def __iter__(self):return selfdef next(self):res = self.aself.a, self.b = self.b, self.a + self.bif self.count > self.n:raise StopIterationself.count += 1return resprint(list(Fib2(10)))
其他迭代器案例

(1) 有很多關于迭代器的例子,比如itertools函數返回的都是迭代器對象。

def test_endless_iter():'''生成無限序列:return:'''from itertools import countcounter = count(start=3)print(next(counter))print(next(counter))print(next(counter))  

(2) 從一個有限序列中生成無限序列

def test_cycle_iter():'''從一個有限序列中生成無限序列:return:'''from itertools import cyclecolors = cycle(['red', 'white', 'blue'])print(next(colors))print(next(colors))print(next(colors))print(next(colors))

總結:

? 對于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即可。

2. 生成器

? 生成器是一個特殊的程序,可以被用作控制循環的迭代行為,python中生成器是迭代器的一種,使用yield返回值函數,每次調用yield會暫停,而可以使用next()函數和send()函數恢復生成器。

?

生成器作用?

? 延遲操作。也就是在需要的時候才產生結果,不是立即產生結果。

什么是生成器?

? 生成器類似于返回值為數組的一個函數,這個函數可以接受參數,可以被調用,但是,不同于一般的函數會一次性返回包括了所有數值的數組,生成器一次只能產生一個值,這樣消耗的內存數量將大大減小,而且允許調用函數可以很快的處理前幾個返回值,因此生成器看起來像是一個函數,但是表現得卻像是迭代器。

? 生成器是包含yield關鍵字的函數。本質上來說,關鍵字yield是一個語法糖,內部實現支持了迭代器協議,同時yield內部是一個狀態機,維護著掛起和繼續的狀態。

python中的生成器

? 要創建一個generator,有很多種方法,第一種方法很簡單,只有把一個列表生成式的[]中括號改為()小括號,就創建一個generator

? 舉例如下:

# 列表生成式
lis = [x * x for x in range(10)]
print(lis)
# 生成器
generator_ex = (x * x for x in range(10))
print(generator_ex)結果:
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
< generator
object < genexpr > at
0x000002A4CBF9EBA0 >#生成器
generator_ex = (x*x for x in range(10))
print(next(generator_ex))
print(next(generator_ex))
print(next(generator_ex))
print(next(generator_ex))#生成器, 可以直接循環得出結果
generator_ex = (x*x for x in range(10))
for i in generator_ex:print(i)結果:
0
1
4
9
生成器調用順序

那么,生成器是怎么調用執行的呢?只需要了解下面幾條規則即可:

a. 當生成器被調用的時候,函數體的代碼不會被執行,而是會返回一個迭代器,其實,生成器函數返回生成器的迭代器。 “生成器的迭代器”這個術語通常被稱作”生成器”。要注意的是生成器就是一類特殊的迭代器。作為一個迭代器,生成器必須要定義一些方法,其中一個就是next()。如同迭代器一樣,我們可以使用next()函數來獲取下一個值。需要明白的是,這一切都是在yield內部實現的。

b. 當next()方法第一次被調用的時候,生成器函數才開始執行,執行到yield語句處停止
next()方法的返回值就是yield語句處的參數(yielded value)
當繼續調用next()方法的時候,函數將接著上一次停止的yield語句處繼續執行,并到下一個yield處停止;如果后面沒有yield就拋出StopIteration異常。

c.每調用一次生成器的next()方法,就會執行生成器中的代碼,知道遇到一個yield或者return語句。yield語句意味著應該生成一個值(在上面已經解釋清楚)。return意味著生成器要停止執行,不在產生任何東西。

d. 生成器的編寫方法和函數定義類似,只是在return的地方改為yield。生成器中可以有多個yield。當生成器遇到一個yield時,會暫停運行生成器,返回yield后面的值。當再次調用生成器的時候,會從剛才暫停的地方繼續運行,直到下一個yield。生成器自身又構成一個循環器,每次循環使用一個yield返回的值。

注意:

(1)生成器是只能遍歷一次的。

(2)生成器是一類特殊的迭代器。

分類:

第一類:生成器函數:

? 生成器函數:也是用def定義的,利用關鍵字yield一次性返回一個結果,阻塞,重新開始

? 還是使用 def 定義函數,但是,使用yield而不是return語句返回結果。yield語句一次返回一個結果,在每個結果中間,掛起函數的狀態,以便下次從它離開的地方繼續執行。

如下案例加以說明:

def Fib3(max):n, a, b = 0, 0, 1while n < max:yield ba, b = b, a + bn = n + 1return '親!沒有數據了...'print("@@@@@@@@@@@@@222")
# 調用方法,生成出10個數來
f=Fib(10)
# 使用一個循環捕獲最后return 返回的值,保存在異常StopIteration的value中
while  True:try:x=next(f)print("f:",x)except StopIteration as e:print("生成器最后的返回值是:",e.value)break

第二類:生成器表達式:

? 類似于列表推導,只不過是把一對大括號[]變換為一對小括號()。但是,生成器表達式是按需產生一個生成器結果對象,要想拿到每一個元素,就需要循環遍歷。

如下案例加以說明:

# 一個列表
xiaoke=[2,3,4,5]
# 生成器generator,類似于list,但是是把[]改為()
gen=(a for a  in xiaoke)
for  i  in gen:print(i)
#結果是:
2
3
4
5

? 為什么要使用生成器?因為效率。使用生成器表達式取代列表推導式可以同時節省 cpu 和 內存(RAM)。如果你構造一個列表(list)的目的僅僅是傳遞給別的函數, 比如 傳遞給tuple()或者set(), 那就用生成器表達式替代吧!

iter

如果一個類想被用于for ... in循環,類似list或tuple那樣,就必須實現一個__iter__()方法,該方法返回一個迭代對象,然后,Python的for循環就會不斷調用該迭代對象的next()方法拿到循環的下一個值,直到遇到StopIteration錯誤時退出循環。

我們以斐波那契數列為例,寫一個Fib類,可以作用于for循環:

class Fib(object):def __init__(self):self.a, self.b = 0, 1 # 初始化兩個計數器a,bdef __iter__(self):return self # 實例本身就是迭代對象,故返回自己def next(self):self.a, self.b = self.b, self.a + self.b # 計算下一個值if self.a > 100000: # 退出循環的條件raise StopIteration();return self.a # 返回下一個值

現在,試試把Fib實例作用于for循環:

>>> for n in Fib():
...     print n
...
1
1
2
3
5
...
46368
75025

getitem

要表現得像list那樣按照下標取出元素,需要實現__getitem__()方法:

class Fib(object):def __getitem__(self, n):a, b = 1, 1for x in range(n):a, b = b, a + breturn a

現在,就可以按下標訪問數列的任意一項了:

>>> f = Fib()
>>> f[0]
1
>>> f[1]
1
>>> f[2]
2
>>> f[3]
3
>>> f[10]
89
>>> f[100]
573147844013817084101

但是list有個神奇的切片方法:

>>> range(100)[5:10]
[5, 6, 7, 8, 9]

對于Fib卻報錯。原因是__getitem__()傳入的參數可能是一個int,也可能是一個切片對象slice,所以要做判斷:

class Fib(object):def __getitem__(self, n):if isinstance(n, int):a, b = 1, 1for x in range(n):a, b = b, a + breturn aif isinstance(n, slice):start = n.startstop = n.stopa, b = 1, 1L = []for x in range(stop):if x >= start:L.append(a)a, b = b, a + breturn L

現在試試Fib的切片:

>>> f = Fib()
>>> f[0:5]
[1, 1, 2, 3, 5]
>>> f[:10]
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

但是沒有對step參數作處理:

>>> f[:10:2]
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]

也沒有對負數作處理,所以,要正確實現一個__getitem__()還是有很多工作要做的。

文件是可迭代對象,也是迭代器

f = open('housing.csv')
from collections import Iterator
from collections import Iterableprint(isinstance(f, Iterator))     #判斷是不是迭代器
print(isinstance(f, Iterable))     #判斷是不是可迭代對象True
True

小結:

  • 凡是可作用于for循環的對象都是Iterable類型;
  • 凡是可作用于next()函數的對象都是Iterator類型,它們表示一個惰性計算的序列;
  • 集合數據類型如listdictstr等是Iterable但不是Iterator,不過可以通過iter()函數獲得一個Iterator對象。

Python3的for循環本質上就是通過不斷調用next()函數實現的,例如:

for x in [1, 2, 3, 4, 5]:pass

實際上完全等價于

# 首先獲得Iterator對象:
it = iter([1, 2, 3, 4, 5])
# 循環:
while True:try:# 獲得下一個值:x = next(it)except StopIteration:# 遇到StopIteration就退出循環break

yield的總結

  (1)通常的for..in...循環中,in后面是一個數組,這個數組就是一個可迭代對象,類似的還有鏈表,字符串,文件。他可以是a = [1,2,3],也可以是a = [x*x for x in range(3)]。

它的缺點也很明顯,就是所有數據都在內存里面,如果有海量的數據,將會非常耗內存。

  (2)生成器是可以迭代的,但是只可以讀取它一次。因為用的時候才生成,比如a = (x*x for x in range(3))。!!!!注意這里是小括號而不是方括號。

  (3)生成器(generator)能夠迭代的關鍵是他有next()方法,工作原理就是通過重復調用next()方法,直到捕獲一個異常。

  (4)帶有yield的函數不再是一個普通的函數,而是一個生成器generator,可用于迭代

  (5)yield是一個類似return 的關鍵字,迭代一次遇到yield的時候就返回yield后面或者右面的值。而且下一次迭代的時候,從上一次迭代遇到的yield后面的代碼開始執行

  (6)yield就是return返回的一個值,并且記住這個返回的位置。下一次迭代就從這個位置開始。

  (7)帶有yield的函數不僅僅是只用于for循環,而且可用于某個函數的參數,只要這個函數的參數也允許迭代參數。

  (8)send()和next()的區別就在于send可傳遞參數給yield表達式,這時候傳遞的參數就會作為yield表達式的值,而yield的參數是返回給調用者的值,也就是說send可以強行修改上一個yield表達式值。

  (9)send()和next()都有返回值,他們的返回值是當前迭代遇到的yield的時候,yield后面表達式的值,其實就是當前迭代yield后面的參數。

  (10)第一次調用時候必須先next()或send(),否則會報錯,send后之所以為None是因為這時候沒有上一個yield,所以也可以認為next()等同于send(None)

轉載于:https://www.cnblogs.com/sunBinary/p/10940974.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/448725.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/448725.shtml
英文地址,請注明出處:http://en.pswp.cn/news/448725.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

初識python之函數基礎

課堂筆記&#xff1a; 1、什么是函數&#xff1f;函數相當于工具&#xff0c;需要事先準備好&#xff0c;在需要用時再使用。2、如何使用函數&#xff1f;函數必須先定義、后調用。3、函數的語法:# def 函數名(參數1,參數2...):# """# 注釋# 函數的說明# 水…

java 的幾種對象 (PO,VO,DAO,BO,POJO) 解釋

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 一、PO:persistant object 持久對象,可以看成是與數據庫中的表相映射的java對象。最簡單的PO就是對應數據庫中某個表中的一條記錄&#x…

【隨想】每日兩題Day.22

題目&#xff1a;102. 二叉樹的層序遍歷 給你二叉樹的根節點 root &#xff0c;返回其節點值的 層序遍歷 。 &#xff08;即逐層地&#xff0c;從左到右訪問所有節點&#xff09;。 示例 1&#xff1a; 輸入&#xff1a;root [3,9,20,null,null,15,7] 輸出&#xff1a;[[3],[…

幫助子女成功的十大路徑

美國全國家長協會(National PTA)建議指出&#xff1a;作為家長您對你子女的成功起著非常重要的影響作用&#xff0c;并舉出幫助子女成功的十種路徑。 1、與子女溝通 如果我們盡早地與子女溝通&#xff0c;提供給他們信息與行為準則&#xff0c;獲得子女的信任&#xff0c;在…

shell關閉指定進程

例如要關閉jupyter-notebook這個進程&#xff1a; ps -ef | grep jupyter-notebook | grep -v grep | cut -c 9-15 | xargs kill -9 說明&#xff1a;管道符“|”用來隔開兩個命令&#xff0c;管道符左邊命令的輸出會作為管道符右邊命令的輸入。 “ps -ef” 查看所有進程  …

垃圾回收算法與垃圾回收器

Java與C等語言最大的技術區別&#xff1a;自動化的垃圾回收機制&#xff08;GC&#xff09; 為什么要了解GC和內存分配策略 1、面試需要 2、GC對應用的性能是有影響的&#xff1b; 3、寫代碼有好處 棧&#xff1a;棧中的生命周期是跟隨線程&#xff0c;所以一般不需要關注 堆&a…

提高孩子睡眠質量 學業事半功倍

睡眠如同大腦的食物。在睡眠期間&#xff0c;許多重要的身體機能靜靜地發生著作用。省略睡眠是有害的&#xff0c;如果一個嚴重缺覺的人開著車&#xff0c;他會臉色蒼白、喜怒無常、反應遲鈍&#xff0c;可能是致命的危險。缺少睡眠讓青少年很難與人相處&#xff0c;學業表現不…

實體類(VO,DO,DTO)的劃分

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 從領域建模中的實體劃分、項目中的實際應用情況兩個角度&#xff0c;對這幾個概念進行簡析。 得出的主要結論是&#xff1a;在項目應用…

IIS新建站點服務器,localhost能登錄但是IP訪問登錄不了。

IIS服務器新建站點之后&#xff0c;瀏覽頁面&#xff0c;服務器本地是可以登錄&#xff0c;但是localhost換成IP就無法訪問。其他站點IP卻可以訪問。 1.如果瀏覽直接失敗&#xff0c;說明端口號需要更換。 2.如果出現IP不能訪問&#xff0c;localhost能訪問&#xff0c;需要在高…

eclipse問題_Alt+/不給提示,只補充代碼問題的解決方案

今天用eclipse敲代碼的時候遇到的問題 我還以為是沖突什么的 還重新裝了軟件 最后才發現原來是快捷鍵設置的問題 解決方案&#xff1a; 1&#xff1a;打開菜單window→Preferences&#xff0c;然后在窗口的左側樹選擇General->Keys 2&#xff1a;在下圖中的5框的地方輸入“w…

領域驅動設計:淺析 VO、DTO、DO、PO 概念、區別、用處

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 本篇文章主要討論一下我們經常會用到的一些對象&#xff1a;VO、DTO、DO和PO。 由于不同的項目和開發人員有不同的命名習慣&#xff0c…

動腦的生活教育

心理學家華生曾經說過&#xff1a;“如果給我一打孩子&#xff0c;我可以把他們變成律師、醫師、科學家&#xff0c;或是強盜、土匪。”華生認為&#xff0c;教育孩子就如同馬戲團的馴獸師訓練野獸一樣&#xff0c;是“刺激”與“反應”的聯結&#xff0c;不需要任何的“思考”…

前端知識點回顧之重點篇——CORS

CORS&#xff08;cross origin resource sharing&#xff09;跨域資源共享 來源&#xff1a;http://www.ruanyifeng.com/blog/2016/04/cors.html 它允許瀏覽器向跨源服務器&#xff0c;發出XMLHttpRequest請求&#xff0c;從而克服了AJAX只能同源使用的限制。 簡介 CORS需要瀏覽…

案例:隱秘而低調的內存泄露(OOM)

內存泄露測試的整個過程如下&#xff1a;在手機里啟動被測APP并打開DDMS。在DDMS中選中【com.example.android.hcgallery】之后單擊按鈕【show heap updates】&#xff0c;然后切換到標簽頁【VM Heap】&#xff0c;再單擊按鈕【Cause GC】。不斷操作APP&#xff0c;并觀察Heap。…

員工價值——如何體現自己價值,如何被自己的領導認可

到公司工作快三年了&#xff0c;比我后來的同事陸續得到了升職的機會&#xff0c;我卻原地不動&#xff0c;心里頗不是滋味。終于有一天&#xff0c;冒著被解聘的危險&#xff0c;我找到老板理論。 “老板&#xff0c;我有過遲到、早退或亂章違紀的現象嗎&#xff1f;”我問。 …

java: PO,VO,TO,BO,DAO,POJO 解釋

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 O/R Mapping 是 Object Relational Mapping&#xff08;對象關系映射&#xff09;的縮寫。通俗點講&#xff0c;就是將對象與關系數據庫綁…

[譯]JavaScript 究竟是如何工作的?(第一部分)

原文地址&#xff1a;How Does JavaScript Really Work? (Part 1)原文作者&#xff1a;Priyesh Patel如果你是一個 JS 開發者或者是正在學習這門語言的學生&#xff0c;很大概率上你會遇到雙字母詞"V8"。在這篇文章中&#xff0c;我將會為你簡述不同的 JS 引擎并深入…

vue實戰(9):總結二

整理前一段所做的工作內容 0.其它 vue實戰&#xff08;1&#xff09;&#xff1a;準備與資料整理vue實戰&#xff08;2&#xff09;&#xff1a;初始化項目、搭建底部導航路由vue實戰&#xff08;3&#xff09;&#xff1a;底部導航顯示、搭建各模塊靜態頁面、添加登錄頁頁面與…

一名IT從業者的英語口語能力成長路徑

這篇文章是我最近十天口語系列文章的合輯&#xff0c;文章比較長&#xff0c;一萬五千余字。但是系統化地歸納了自己十多年的英語尤其是口語方面的學習經歷與總結思考。我不是個純粹的英語專業學生&#xff0c;我甚至不是任何英語相關專業的學生&#xff0c;但是我和英語卻有著…

解決:SpringBoot 錯誤:Caused by: org.yaml.snakeyaml.scanner.ScannerException

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 錯誤: Caused by: org.yaml.snakeyaml.scanner.ScannerException: while scanning for the next tokenfound character that cannot s…