python的迭代器无法输出值_python迭代器和生成器
人生還早,誰能笑到最后呢,堅持吧!
1.迭代器協議
由于生成器自動實現了迭代器協議,我們有必要了解迭代器協議是什么,才能更好的理解生成器。
1)迭代器協議:對象要提供__next()__方法,它要么返回迭代中的下一個對象,要么引起一個StopIteration錯誤,終止迭代
2)可迭代對象:實現了迭代器協議的對象
3)協議是一種約定,可迭代對象實現了迭代器協議,python的內部工具(如for循環,sum,min,max函數等)使用迭代器協議訪問對象。
for循環的本質:循環所有對象,全都是使用迭代器協議
(字符串,列表,元組,字典,集合,文件對象)這些都不是可迭代對象,只不過在for循環式,調用了他們內部的__iter__方法,把他們變成了可迭代對象
list = ['a','b',1,2]
# for i in list:
# print(i)
list_iter = list.__iter__()#調用__iter__方法將list對象變成可迭代對象
print(list_iter.__next__())依次調用,知道引起StopIteration錯誤
print(list_iter.__next__())
print(list_iter.__next__())
print(list_iter.__next__())
用while循環模擬for循環做的事情
list_iter = list.__iter__()
while True:
try:
print(list_iter.__next__())
except StopIteration:
print('迭代完畢,可以終止')
break
迭代器對象的本質是一種數據流,它只是一個有序的序列,我們不能提前知道它的長度,只有不斷通過__next__()取出下一個值。
Interator甚至可以表示一個無限大的數據流,例如全體自然數。而使用list是永遠不可能存儲全體自然數的。
優點
1):迭代器提供了一種不依賴于索引的取值方式,這樣就可以遍歷那些沒有索引的可迭代對象了(字典,集合,文件)
2):迭代器與列表比較,迭代器是惰性計算的,,只有在需要返回下一個數據時才計算,更節省內存
缺點:
1):無法獲取迭代器的長度,使用不如列表索引取值靈活
2):一次性的,只能往后取值,不能倒著取值
如何查看一個對象是不是可迭代對象和迭代器對象
from collections import Iterable,Iterator#導入模塊
list = ['a','b',1,2]
list_iter = list.__iter__()
#isinstance
print(isinstance(list,Iterable))#是否是可迭代對象
print(isinstance(list,Iterator))#是否是迭代器對象
print(isinstance(list_iter,Iterable))
print(isinstance(list_iter,Iterator))
小結:
凡是可作用于for循環的對象都是Iterable類型;
凡是可作用于next()函數的對象都是Iterator類型,它們表示一個惰性計算的序列,也就是說你需要取值時才給你取;
集合數據類型如list、dict、str等是Iterable但不是Iterator,不過可以通過iter()函數獲得一個Iterator對象。
協程函數
如果在一個函數內部yield的使用方式是表達式形式的話,如x=yield,那么該函數稱為協程函數,比線程更小
def eater(name):
food_list=[]
while True:
food=yield food_list
food_list.append(food)#因為有yied
e=eater('鐵根') #一個生成器的內存地址
print(next(e)) #初始化迭代器
print(e.send('包子'))
print(e.send('韭菜餡包子'))
print(e.send('大蒜包子'))
2.生成器
通過列表生成式,我們可以創建一個列表。但是我們知道受內存的限制,我們的列表容量是有限的,而且創建包含100萬個元素的列表,我們只用到前幾個,剩下的都是浪費內存空間。
所以,如果列表元素可以按照某種算法推算出來,那我們是否可以在循環的過程中不斷推算出后續的元素呢?這樣就不必創建完整的list,從而節省大量的空間。在Python中,這種一邊循環一邊計算的機制,稱為生成器:generator。
要創建一個generator,有很多種方法。第一種方法很簡單,只要把一個列表生成式的[]改成(),就創建了一個generator:
l=[x*x for x in range(10)]
print(l)
g=(x*x for x in range(10))
print(g)
打印結果
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
at 0x0000000001E70F10>
取generator值用__next__()一個一個打印出來
print(g.__next__())
print(g.__next__())
print(g.__next__())
print(g.__next__())
print(g.__next__())
print(g.__next__())
0
1
4
9
16
25
generator也是可迭代對象,所以也可以使用for循環
for i in g:
print(i)
0
1
4
9
16
25
36
49
64
81
所以,我們創建了一個generator后,基本上永遠不會調用next(),而是通過for循環來迭代它,并且不需要關心StopIteration的錯誤。
generator非常強大。如果推算的算法比較復雜,用類似列表生成式的for循環無法實現的時候,還可以用函數來實現。
比如,著名的斐波拉契數列(Fibonacci),除第一個和第二個數外,任意一個數都可由前兩個數相加得到:
1, 1, 2, 3, 5, 8, 13, 21, 34, ...
斐波拉契數列用列表生成式寫不出來,但是,用函數把它打印出來卻很容易:
def fib(max):
n, a, b = 0, 0, 1
while n < max:
print(b)
a, b = b, a + b
n = n + 1
return 'done'
fib(20)
1
1
2
3
5
8
13
21
34
55
89
144
233
377
610
987
1597
2584
4181
6765
仔細觀察,可以看出,fib函數實際上是定義了斐波拉契數列的推算規則,可以從第一個元素開始,推算出后續任意的元素,這種邏輯其實非常類似generator。
也就是說,上面的函數和generator僅一步之遙。要把fib函數變成generator,只需要把print(b)改為yield b就可以了:
def fib(max):
n, a, b = 0, 0, 1
while n < max:
yield b
a, b = b, a + b
n = n + 1
return 'done'
f = fib(6)
print(f)
這里,最難理解的就是generator和函數的執行流程不一樣。函數是順序執行,遇到return語句或者最后一行函數語句就返回。而變成generator的函數,在每次調用next()的時候執行,遇到yield語句返回,再次執行時從上次返回的yield語句處繼續執行。
定義generator的另一種方法。如果一個函數定義中包含yield關鍵字,那么這個函數就不再是一個普通函數,而是一個generator
我們把函數變成generator后,基本上也不再使用__next__()方法,都是用for循環來迭代,但是用for循環調用generator時,發現拿不到generator的return語句的返回值。
def fib(max):
n, a, b = 0, 0, 1
while n < max:
# print(b)
yield b
a, b = b, a + b
n = n + 1
return 'done'
for i in fib(5):
print(i)
1
1
2
3
5
如果想要拿到返回值,必須捕獲StopIteration錯誤,返回值包含在StopIteration的value中:
while True:
try:
x=next(g)
print(x)
except StopIteration as e:
print('Generator return value:',e.value)
break
2
3
5
Generator return value: done
看到一張圖方便理解列表等容器,生成器,可迭代對象,迭代器之間的關系
小結:
generator可以是表達式也可以是函數,它保存的是算法,同時它是iterable對象,它也是一個generator。
總結
以上是生活随笔為你收集整理的python的迭代器无法输出值_python迭代器和生成器的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python归并排序 分词_python
- 下一篇: 云服务器apache mysql php