首頁  >  文章  >  後端開發  >  python生成器與迭代器的區別

python生成器與迭代器的區別

爱喝马黛茶的安东尼
爱喝马黛茶的安东尼原創
2019-06-24 13:26:313551瀏覽

對於list、string、tuple、dict等這些容器物件,使用for迴圈遍歷是很方便的。在後台for語句對容器物件呼叫iter()函數。 iter()是python內建函數。 iter()函數會傳回一個定義了next()方法的迭代器對象,它在容器中逐一存取容器內的元素。 next()也是python內建函數。在沒有後續元素時,next()會拋出一個StopIteration異常,通知for語句循環結束。

python生成器與迭代器的區別

#迭代器是用來幫助我們記錄每次迭代訪問到的位置,當我們對迭代器使用next()函數的時候,迭代器會向我們傳回它所記錄位置的下一個位置的資料。實際上,在使用next()函數的時候,呼叫的就是迭代器物件的_next_方法(Python3中是物件的_next_方法,Python2中是物件的next()方法)。所以,我們要建構一個迭代器,就要實現它的_next_方法。但這還不夠,python要求迭代器本身也是可迭代的,所以我們還要為迭代器實作_iter_方法,而_iter_方法要回傳一個迭代器,迭代器本身正是一個迭代器,所以迭代器的_iter_方法返回自身self即可。

一些術語的解釋:

1,迭代器協定:物件需要提供next()方法,它要麼返回迭代中的下一項,要麼就引起一個StopIteration異常,以終止迭代。
2,可迭代物件:實作了迭代器協定物件。 list、tuple、dict都是Iterable(可迭代物件),但不是Iterator(迭代器物件)。但可以使用內建函數iter() ,把這些都變成Iterable(可迭代器物件)。
3,for item in Iterable 迴圈的本質就是先透過iter()函數取得可迭代物件Iterable的迭代器,然後對取得到的迭代器不斷呼叫next()方法來取得下一個值並將其賦值給item,當遇到StopIteration的異常後循環結束。

相關推薦:《Python影片教學

Python自備容器物件案例:

# 随便定义一个list
listArray=[1,2,3]
# 使用iter()函数
iterName=iter(listArray)
print(iterName)
# 结果如下:是一个列表list的迭代器
# <list_iterator object at 0x0000017B0D984278>
 
print(next(iterName))
print(next(iterName))
print(next(iterName))
print(next(iterName))#没有迭代到下一个元素,直接抛出异常
# 1
# 2
# 3
# Traceback (most recent call last):
# File "Test07.py", line 32, in <module>
# StopIteration

Python中一個實作了_iter_方法和_next_方法的類別對象,就是迭代器,以下案例是計算菲波那切數列的案例

class Fib(object):
 def __init__(self, max):
  super(Fib, self).__init__()
  self.max = max
 
 def __iter__(self):
  self.a = 0
  self.b = 1
  return self
 
 def __next__(self):
  fib = self.a
  if fib > self.max:
   raise StopIteration
  self.a, self.b = self.b, self.a + self.b
  return fib
 
# 定义一个main函数,循环遍历每一个菲波那切数
def main():
 # 20以内的数
 fib = Fib(20)
 for i in fib:
  print(i)
 
# 测试
if __name__ == &#39;__main__&#39;:
 main()

解釋說明:

在本類的實現中,定義了一個_iter_(self)方法,這個方法是在for迴圈時被iter()調用,傳回一個迭代器。因為在遍歷的時候,是直接呼叫的python內建函數iter() ,由iter()透過呼叫_iter_(self)來獲得物件的迭代器。有了迭代器,就可以逐一遍歷元素了。而逐一遍歷的時候,也是使用內建的next()函數透過呼叫物件的_next_(self)方法來對迭代器物件進行遍歷。所以要實作_iter_(self)和_next_(self)這兩個方法。

而且因為實作了_next_(self)方法,所以在實作_iter_(self)的時候,直接回傳self就可以。

總結一句話就是: 

在循環遍歷自訂容器物件時,會使用python內建函數iter()呼叫遍歷物件的_iter_(self)取得一個迭代器,之後再循環對這個迭代器使用next()呼叫迭代器物件的_next_(self) 。

注意點: _iter_(self)只會被呼叫一次,而_next_(self)會被呼叫 n 次,直到出現StopIteration例外。 

產生器 

#作用:

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

注意事項:

產生器是只能遍歷一次的。
生成器是一類特殊的迭代器。

分類:

#

第一类:生成器函数:还是使用 def 定义函数,但是,使用yield而不是return语句返回结果。yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次从它离开的地方继续执行。

# 菲波那切数列
def Fib(max):
 n, a, b = 0, 0, 1
 while n < max:
  yield b
  a, b = b, a + b
  n = n + 1
 return &#39;亲!没有数据了...&#39;
# 调用方法,生成出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(), 那就用生成器表达式替代吧! 
#本案例是直接把列表转化为元组
kk=tuple(a for a in xiaoke)
print(kk)
#结果是:
(2, 3, 4, 5) 
# python内置的一些函数,可以识别这是生成器表达式,外面有一对小括号,就是生成器
result1=sum(a for a in range(3))
print(result1)
# 列表推导式
result2=sum([a for a in range(3)])
print(result2)

区别:

生成器能做到迭代器能做的所有事,而且因为自动创建了 iter()和 next()方法,生成器显得特别简洁,而且生成器也是高效的,使用生成器表达式取代列表解析可以同时节省内存。除了创建和保存程序状态的自动方法,当发生器终结时,还会自动抛出 StopIteration 异常。

以上是python生成器與迭代器的區別的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn