函數進階之生成器和叠代器
前提:
列表生成式
給列表a裏的大於5的每一個值加10
a = [1, 2, 5, 6, 7, 8] a = [i + 10 if i > 5 else i for i in a] # 可以循環任何可循環的東西,不過只能寫到列表或元組裏。 print(a) # [1, 2, 5, 16, 17, 18]
復雜東西列表生成式寫不出來,最多到三元運算了。
正文:
生成器是為了省內存,不一次釋放,需要一個取一個。
g = (i for i in range(5)) print(g) # <generator object <genexpr> at 0x101fba048>print(next(g)) # 0 print(next(g)) # 1 print(next(g)) # 2 print(next(g)) # 3 print(next(g)) # 4 print(next(g)) # 到最後沒有了,會報錯
生成器只能往前走,不能後退。
我們創建了一個generator後,基本上永遠不會調用next(),而是通過for循環來叠代它,並且不需要關心StopIteration的錯誤,for循環會把它自動消化。
g2 = (i for i in range(5)) for i in g2: print(i)
generator非常強大。如果推算的算法比較復雜,用類似列表生成式的for循環無法實現的時候,還可以用函數來實現。
生成器和range
python2裏range是直接創建列表。
python3裏的range是用生成器做的,是循環到了才創建。
python2裏有個xrange,其實就是等於python3裏的range。python3只有range,等於是把python2裏的那個range去掉了。
用生成器實現斐波那契
def fib(max): n, a, b = 10, 0, 1 while n < max: print(‘before yield‘)yield n # 把函數的執行過程凍結在這一步,並且把b的值,返回給外面的next() print(b) a, b = b, a + b n += 1 return ‘done‘ print(fib(15)) # <generator object fib at 0x101fba048> 裏面有yield,函數名加括號,內部代碼根本不執行,只是生成一個生成器對象。 f = fib(15) # return function into a generator next(f) # before yield # first time call next() n = next(f) # 1 # before yield next(f) # 1 # before yield next(f) # 2 # before yield print(n) # 11
yield的作用是把函數裏面的值返回出來到外部
解析:第一次print出 before yield之後,遇到yield,程序終止,再次執行next(f),程序繼續運行print出b的值,然後直到再次走到print(‘before yield‘)後,
程序遇到yield n,程序終止,然後再次執行next(f),以此循環......
生成器的創建方式
1. "列表"生成式(),最多支持三元表達式。 例:g2 = (i for i in range(5))
2. 函數
用生成器實現range方法
def range2(n): count = 0 while count < n: print(‘count‘, count) yield count count += 1 new_range = range2(10) r1 = next(new_range) print(r1) # 0 r2 = new_range.__next__() # 和 next(new_range)是一樣的 print(r2) # 1
yield vs return
return 返回並終止函數
yield 返回數據,並凍結當前的執行過程
next 喚醒凍結的函數執行過程,繼續執行,直到遇到下一個yield
函數有了yield之後
1.函數名加()就變成了一個生成器
2.return在生成器裏,代表生成器的終止,直接報錯
生成器和文件操作
with open(‘test.txt‘) as f: for line in f: pass
其實在for循環這個文件的時候,就是在循環一個生成器。
生成器send方法
def range2(n): count = 0 while count < n: print(‘count‘, count) sign = yield count count += 1 if sign == ‘stop‘: print(‘----sign ‘, sign) break new_range = range2(10) n1 = next(new_range) # count 0 n2 = new_range.__next__() # count 1 n3 = new_range.send(None) # count 2 這條語句等於next,因為next(或__next__)方法就是默認往生成器裏面發送了一個None n4 = new_range.send(‘stop‘) # 終止,程序報錯,打印----sign stop
send的作用:
1.喚醒並繼續執行
2. 發送一個信息到生成器的內部
next()默認往生成器裏發送一個None
叠代器
可以被next()函數調用並不斷返回下一個值的對象稱為叠代器:Iterator。
可以使用isinstance()判斷一個對象是否是Iterable對象
from collections import Iterable print(isinstance(123, Iterable)) # False print(isinstance(‘abc‘, Iterable)) # True
只有生成器是叠代器。
from collections import Iterator li = [i for i in range(10)] print(isinstance(li, Iterator)) # False
但是可以把列表、字符串等變成叠代器
li = iter(li) print(li.__next__()) # 0 print(li.__next__()) # 1
Python的Iterator對象表示的是一個數據流,Iterator對象可以被next()函數調用並不斷返回下一個數據,直到沒有數據時拋出StopIteration錯誤。
可以把這個數據流看做是一個有序序列,但我們卻不能提前知道序列的長度,只能不斷通過next()函數實現按需計算下一個數據,所以Iterator的計算是惰性的,
只有在需要返回下一個數據時它才會計算。Iterator甚至可以表示一個無限大的數據流,例如全體自然數。而使用list是永遠不可能存儲全體自然數的。
需要註意的是:
1.列表用len能知道長度,但是叠代器不能知道
2.叠代器比生成器的範圍要大一些,學了面向對象後不用生成器也能next。
凡是可作用於for循環的對象都是Iterable類型;
凡是可作用於next()函數的對象都是Iterator類型,它們表示一個惰性計算的序列;
集合數據類型如list、dict、str等是Iterable但不是Iterator,不過可以通過iter()函數獲得一個Iterator對象。
Python3的for循環本質上就是通過不斷調用next()函數實現的
生成器算得上是Python語言中最吸引人的特性之一,生成器其實是一種特殊的叠代器,不過這種叠代器更加優雅。
它不需要再像上面的類一樣寫__iter__()和__next__()方法了,只需要一個yiled關鍵字。
生成器一定是叠代器(反之不成立),因此任何生成器也是以一種懶加載的模式生成值。
函數進階之生成器和叠代器