亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

深入講解Python中的迭代器和生成器

系統 2039 0

在Python中,很多對象都是可以通過for語句來直接遍歷的,例如list、string、dict等等,這些對象都可以被稱為可迭代對象。至于說哪些對象是可以被迭代訪問的,就要了解一下迭代器相關的知識了。

迭代器

迭代器對象要求支持迭代器協議的對象,在Python中,支持迭代器協議就是實現對象的__iter__()和next()方法。其中__iter__()方法返回迭代器對象本身;next()方法返回容器的下一個元素,在結尾時引發StopIteration異常。

__iter__()和next()方法

這兩個方法是迭代器最基本的方法,一個用來獲得迭代器對象,一個用來獲取容器中的下一個元素。

對于可迭代對象,可以使用內建函數iter()來獲取它的迭代器對象:

深入講解Python中的迭代器和生成器_第1張圖片

例子中,通過iter()方法獲得了list的迭代器對象,然后就可以通過next()方法來訪問list中的元素了。當容器中沒有可訪問的元素后,next()方法將會拋出一個StopIteration異常終止迭代器。

其實,當我們使用for語句的時候,for語句就會自動的通過__iter__()方法來獲得迭代器對象,并且通過next()方法來獲取下一個元素。

自定義迭代器

了解了迭代器協議之后,就可以自定義迭代器了。

下面例子中實現了一個MyRange的類型,這個類型中實現了__iter__()方法,通過這個方法返回對象本身作為迭代器對象;同時,實現了next()方法用來獲取容器中的下一個元素,當沒有可訪問元素后,就拋出StopIteration異常。

            
class MyRange(object):
 def __init__(self, n):
  self.idx = 0
  self.n = n

 def __iter__(self):
  return self

 def next(self):
  if self.idx < self.n:
   val = self.idx
   self.idx += 1
   return val
  else:
   raise StopIteration()

class MyRange(object):
 def __init__(self, n):
  self.idx = 0
  self.n = n
 
 def __iter__(self):
  return self
 
 def next(self):
  if self.idx < self.n:
   val = self.idx
   self.idx += 1
   return val
  else:
   raise StopIteration()


          

這個自定義類型跟內建函數xrange很類似,看一下運行結果:

            
myRange = MyRange(3)
for i in myRange:
 print i


          

20151026160048402.jpg (437×70)

迭代器和可迭代對象

在上面的例子中,myRange這個對象就是一個可迭代對象,同時它本身也是一個迭代器對象。

看下面的代碼,對于一個可迭代對象,如果它本身又是一個迭代器對象,就會有下面的 問題,就沒有辦法支持多次迭代。

20151026160106053.jpg (624×100)

為了解決上面的問題,可以分別定義可迭代類型對象和迭代器類型對象;然后可迭代類型對象的__iter__()方法可以獲得一個迭代器類型的對象。看下面的實現:

            
class Zrange:
 def __init__(self, n):
  self.n = n

 def __iter__(self):
  return ZrangeIterator(self.n)

class ZrangeIterator:
 def __init__(self, n):
  self.i = 0
  self.n = n

 def __iter__(self):
  return self

 def next(self):
  if self.i < self.n:
   i = self.i
   self.i += 1
   return i
  else:
   raise StopIteration() 

zrange = Zrange(3)
print zrange is iter(zrange)   

print [i for i in zrange]
print [i for i in zrange]


          


代碼的運行結果為:

20151026160126302.jpg (510×75)

其實,通過下面代碼可以看出,list類型也是按照上面的方式,list本身是一個可迭代對象,通過iter()方法可以獲得list的迭代器對象:

深入講解Python中的迭代器和生成器_第2張圖片

生成器

在Python中,使用生成器可以很方便的支持迭代器協議。生成器通過生成器函數產生,生成器函數可以通過常規的def語句來定義,但是不用return返回,而是用yield一次返回一個結果,在每個結果之間掛起和繼續它們的狀態,來自動實現迭代協議。

也就是說,yield是一個語法糖,內部實現支持了迭代器協議,同時yield內部是一個狀態機,維護著掛起和繼續的狀態。

下面看看生成器的使用:

深入講解Python中的迭代器和生成器_第3張圖片

在這個例子中,定義了一個生成器函數,函數返回一個生成器對象,然后就可以通過for語句進行迭代訪問了。

其實,生成器函數返回生成器的迭代器。 “生成器的迭代器”這個術語通常被稱作”生成器”。要注意的是生成器就是一類特殊的迭代器。作為一個迭代器,生成器必須要定義一些方法,其中一個就是next()。如同迭代器一樣,我們可以使用next()函數來獲取下一個值。

生成器執行流程

下面就仔細看看生成器是怎么工作的。

從上面的例子也可以看到,生成器函數跟普通的函數是有很大差別的。

結合上面的例子我們加入一些打印信息,進一步看看生成器的執行流程:

深入講解Python中的迭代器和生成器_第4張圖片

通過結果可以看到:

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

在開始介紹生成器表達式之前,先看看我們比較熟悉的列表解析( List comprehensions),列表解析一般都是下面的形式。

            
[expr for iter_var in iterable if cond_expr]


          

迭代iterable里所有內容,每一次迭代后,把iterable里滿足cond_expr條件的內容放到iter_var中,再在表達式expr中應該iter_var的內容,最后用表達式的計算值生成一個列表。

例如,生成一個list來保護50以內的所以奇數:

            
[i for i in range(50) if i%2]


          

生成器表達式是在python2.4中引入的,當序列過長, 而每次只需要獲取一個元素時,應當考慮使用生成器表達式而不是列表解析。生成器表達式的語法和列表解析一樣,只不過生成器表達式是被()括起來的,而不是[],如下:

            
(expr for iter_var in iterable if cond_expr)


          

看一個例子:

深入講解Python中的迭代器和生成器_第5張圖片

生成器表達式并不是創建一個列表, 而是返回一個生成器,這個生成器在每次計算出一個條目后,把這個條目”產生”(yield)出來。 生成器表達式使用了”惰性計算”(lazy evaluation),只有在檢索時才被賦值(evaluated),所以在列表比較長的情況下使用內存上更有效。

繼續看一個例子:

深入講解Python中的迭代器和生成器_第6張圖片

從這個例子中可以看到,生成器表達式產生的生成器,它自身是一個可迭代對象,同時也是迭代器本身。

遞歸生成器

生成器可以向函數一樣進行遞歸使用的,下面看一個簡單的例子,對一個序列進行全排列:

            
def permutations(li):
 if len(li) == 0:
  yield li
 else:
  for i in range(len(li)):
   li[0], li[i] = li[i], li[0]
   for item in permutations(li[1:]):
    yield [li[0]] + item

for item in permutations(range(3)):
 print item

def permutations(li):
 if len(li) == 0:
  yield li
 else:
  for i in range(len(li)):
   li[0], li[i] = li[i], li[0]
   for item in permutations(li[1:]):
    yield [li[0]] + item
 
for item in permutations(range(3)):
 print item


          

?生成器的send()和close()方法

生成器中還有兩個很重要的方法:send()和close()。

send(value):
從前面了解到,next()方法可以恢復生成器狀態并繼續執行,其實send()是除next()外另一個恢復生成器的方法。

Python 2.5中,yield語句變成了yield表達式,也就是說yield可以有一個值,而這個值就是send()方法的參數,所以send(None)和next()是等效的。同樣,next()和send()的返回值都是yield語句處的參數(yielded value)

關于send()方法需要注意的是:調用send傳入非None值前,生成器必須處于掛起狀態,否則將拋出異常。也就是說,第一次調用時,要使用next()語句或send(None),因為沒有yield語句來接收這個值。

close():
這個方法用于關閉生成器,對關閉的生成器后再次調用next或send將拋出StopIteration異常。

下面看看這兩個方法的使用:

深入講解Python中的迭代器和生成器_第7張圖片

總結

本文介紹了Python迭代器和生成器的相關內容。

  • 通過實現迭代器協議對應的__iter__()和next()方法,可以自定義迭代器類型。對于可迭代對象,for語句可以通過iter()方法獲取迭代器,并且通過next()方法獲得容器的下一個元素。
  • 像列表這種序列類型的對象,可迭代對象和迭代器對象是相互獨立存在的,在迭代的過程中各個迭代器相互獨立;但是,有的可迭代對象本身又是迭代器對象,那么迭代器就沒法獨立使用。
  • itertools模塊提供了一系列迭代器,能夠幫助用戶輕松地使用排列、組合、笛卡爾積或其他組合結構。
  • 生成器是一種特殊的迭代器,內部支持了生成器協議,不需要明確定義__iter__()和next()方法。
  • 生成器通過生成器函數產生,生成器函數可以通過常規的def語句來定義,但是不用return返回,而是用yield一次返回一個結果。

更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 日本高清在线观看天码888 | 五月婷婷婷婷 | 美女美女高清毛片视频 | 久久精品国产一区二区三区 | 日本 毛片基地-亚洲 | 久久天天躁狠狠躁夜夜爽蜜月 | 国产精品资源网站在线观看 | 97福利视频在线观看 | 拍真实国产伦偷精品 | 久青草视频97国内免费影视 | 毛片免费全部免费观看 | 国产欧美亚洲精品第二区首页 | 日本成本人在线观看免费视频 | 亚洲免费美女视频 | 国产aaaaaa| 奇米影视777在线观看 | 久久香蕉国产线看观看8青草 | 国产亚洲女人久久久久久 | 日日碰碰 | 伊人久久精品成人网 | 国产片一级aaa毛片视频 | 一级免费看| 久久国产精品免费看 | 欧美一级毛片生活片 | 亚洲精品国产手机 | 狠狠色噜噜狠狠狠8888米奇 | 久久精品国产亚洲妲己影院 | 精品久久久久久国产 | 亚洲国产精品日韩高清秒播 | 久久国产免费 | 亚洲精品宾馆在线精品酒店 | 天天操综合 | 国产精品视频全国免费观看 | 日韩一区二区在线免费观看 | 在线不卡一区二区 | 99久久综合国产精品免费 | 国产精品高清久久久久久久 | 印度最a级毛片 | 欧洲色网 | 99视频国产在线 | 亚洲国产精品久久 |