首先廖雪峰網站寫的內容就我目前初步學習來說,已經相當詳實,知識點平鋪直敘讓人易接受,所以以下內容均作為一種摘記記錄以及補充。
1. 列表生成器
主要目的是建立 list 。多看例子就能清楚:
print(list(range(1,10,2))) #[1, 3, 5, 7, 9].生成1~9(左閉右開),相隔為2 print([t * t for t in range(1,10,3) if t % 2 == 0]) #[16].生成1~9相隔4,且是偶數的平方和 print([m + n for m in '123' for n in '456']) #['14', '15', '16', '24', '25', '26', '34', '35', '36'].全排列 d = {'x': 'A', 'y': 'B', 'z': 'C'} for k, v in d.items(): print(k, '=', v) print([m + '=' + n for m,n in d.items()]) #['x=A', 'y=B', 'z=C'] #篩選單詞,並全變小寫 L = ['Hello', 'World', 18, 'Apple', None] print([t.lower() for t in L if isinstance(t, str)]) #['hello', 'world', 'apple']
2. Iterable vs Iterator
iterable 是可迭代物件,iterator 是迭代器。兩者都是 collection.abc 中得抽象類。iterator 繼承自 iterable 。
- iterable 有常見得 list,dict,str,tuple 等或者自定義的類(該類必須實現抽象方法 _iter()_)。當一個可迭代物件作為引數呼叫自身的 iter() 方法時,會返回一個迭代器。迭代器擁有 _next()_ 抽象方法,可迭代物件沒有,通過該方法就可以逐個得到 “序列” 中的各個值,不斷呼叫 _next()_ 方法,最後會引起 StopIteration 異常報錯,代表迭代結束了。同時迭代器還擁有 _iter()_ 方法,所以迭代器也是個可迭代物件。即所有的迭代器都是可迭代物件,但是可迭代物件並不都是迭代器,基本判斷方法是是否呼叫 next() 方法,list,dect,str,tuple 都並不行,即不是迭代器。
我們可以通過 isinstance 來判斷:
from collections.abc import Iterable,Iterator t = [1,2,3] #列表 print(isinstance(t, Iterable)) #true print(isinstance(t, Iterator)) #false
我們常用的 for...in [ ] 。就是利用了迭代器
from collections.abc import Iterable,Iterator L = [1,2,3] print(isinstance(L, Iterator)) #Flase for t in L: print(t, end=' ') #1 2 3
這是我們常寫的程式碼,輸出123。既然 L 不是迭代器為啥也能迭代輸出呢。這就是在使用 for...in 的時候,Python 直譯器主動將可迭代物件呼叫了 iter() 返回迭代器,即每次都是通過迭代器的 next() 方法進行輸出。那麼哪個異常 StopIteration 呢?異常應該被 for...in 內部處理了,並不顯式的丟擲。
我們換一種更明顯的寫法:
1 from collections.abc import Iterable,Iterator 2 L = [1,2,3] 3 print(isinstance(L, Iterator)) #false 4 T = L.__iter__() 5 print(isinstance(T, Iterator)) #true。現在T就是迭代器了,擁有了next()方法。 6 print(T.__next__()) # 1 7 print(T.__next__()) # 2 8 print(T.__next__()) # 3 9 print(T.__next__()) # StopIteration
結果和我們想的是一樣的。或者再這樣寫
1 from collections.abc import Iterable,Iterator 2 L = [1,2,3] 3 print(isinstance(L, Iterator)) #false 4 T = iter(L) 5 while True: 6 try: 7 print(next(T), end=' ') 8 except StopIteration: 9 print('結束') 10 break
(點選圖片檢視原文)
3. 生成器和 yield
- 生成器是返回一個 generator iterator 的函式。但是這個函式中包含 yield 表示式,除此之外別無它異,用來產生一系列供 for 迴圈使用的值或者通過 next() 逐一獲取。所以生成器一般也稱為生成器函式。
- 生成迭代器 generator iterator 是由生成器 generator 建立的物件。每遇到 yield 會暫停(相當於return),並記住當前位置,之後在繼續在記住的位置繼續向下執行。而不同於普通函式每次都由上往下執行。
- 第一種建立生成器的方法:將列表生成式的 [ ] 換成 ( )
L = ( t * t for t in range(1,10)) print(L) #<generator object <genexpr> at 0x0000028D2F68B840> print(next(L)) # 1 print(next(L)) # 4 for i in L: # 迭代輸出 print(i)
- 當一個生成函式被呼叫時,返回一個迭代器,成為生成器。這個生成器來控制生成函式的執行,遇到 yield 就掛起,下次繼續從 掛起處執行。前面說過迭代器有 next() 方法,所以這裡的yield 就是幹了 next() 方法的事。一樣不斷next 直到無資料 StopIteration。
- 第二種是通過定義函式:
1 def test(): 2 print('1') 3 yield 4 print('2') 5 yield 6 print('3') 7 yield 8 9 t = test() # t 是生成器,生成器來控制函式 10 print(t) # <generator object test at 0x0000021EF6C5B840> 11 next(t) # 1 12 next(t) # 2 13 next(t) # 3 14 next(t) # StopIteration
可以通過 11~14 行看出,yield 起的作用就是掛起。第一次呼叫next() 方法,函式執行到第三句就停了,第二次呼叫 next() 執行到第五句。yield 就像是 OS 中的中斷語句,保護現場--恢復現場。
再來看一個斐波那契例子:
1 #斐波那契數列 2 #常規寫法一: 3 # def fib(max): 4 # n, a, b = 0, 0, 1 5 # while n < max: 6 # print(b) 7 # a, b = b, a + b 8 # n = n + 1 9 # return 'done' 10 # 11 # fib(6) 12 13 #生成器寫法二: 14 from collections.abc import Iterator,Iterable 15 def fib(max): 16 n, a, b = 0, 0, 1 17 while n < max: 18 yield b # yield 類似於return 將 b 返回 19 a, b = b, a + b 20 n = n + 1 21 return 'done' 22 23 f = fib(6) 24 print(f) #<generator object fib at 0x00000124DDD8B840> 25 print(isinstance(f, Iterator)) #True。生成函式返回迭代器 26 27 for n in f: 28 print(n, end=' ') #1 1 2 3 5 8
通過觀察24,25行可以知道,呼叫了 fib() 之後,函式並沒有執行到尾(否則返回 str = 'done'),正如上文所說,返回的是一個 生成器,也就是呼叫生成函式(含 yield語句的)返回生成器,然後我們通過生成器來控制函式的執行。只有執行27行的 for...in 的時候,才會去執行15~21這段函式程式碼。
具體執行過程:第一次從16至18行停止,因為yield的存在,執行到18行就停了,然後返回一個值 b 給 for 迴圈,然後執行28行輸出 b,然後next()迭代器繼續從上次停止的地方的下一行19行繼續執行(迭代器next()只要不是StopIteration 或者生成函式結束了,for 迴圈就得以繼續),然後在while迴圈內,再次執行到18行停止,返回 b 給 for。繼續重複,直至跳出while迴圈,fib() 這段生成器函式結束了,for...in 也就結束了。
1 # 生成器寫法三 2 from collections.abc import Iterator,Iterable 3 def fib(max): 4 n, a, b = 0, 0, 1 5 while n < max: 6 yield b # yield 類似於return 將 b 返回 7 a, b = b, a + b 8 n = n + 1 9 10 f = fib(3) 11 print(f) #<generator object > 12 print(isinstance(f, Iterator)) #True。生成函式返回迭代器 13 print(next(f)) # 1 14 print(next(f)) # 1 15 print(next(f)) # 2 16 print(next(f)) # StopIteration
4.生成器send()方法
stackoverflow 上還有關於生成 yield 配合使用 send()的方法。查閱官網,send(value) 函式意思:恢復執行,並向生成器傳送一個值,value 引數將被當作 yield 表示式結果。
1 def test(): 2 while True: 3 x = yield 4 yield x * 2 5 6 g = test() 7 print(next(g)) # none 8 print(g.send(12)) #24
我們已經知道 yield 可以當作return來理解。
首先第六行建立了 g (生成器),第七行輸出 none,因為執行第七行,也就是去執行test()函式了,函式順利執行到第三行,3 = yield 明顯是我們學的賦值語句,難道是將yield賦值給3?不是的。先解釋輸出的none,因為沒有引數寫在yield的右邊,即沒有引數返回,所以第七行輸出 None。同時因為yield存在而停止繼續。
而第八行:遇到g.send() 會繼續執行上次執行到第三行的地方,這裡傳入的引數12就是賦值給x的。所以再往下第四行,yiled看成return 返回12*2,同時test()函式被掛起,返回24給第八行。至此函式結束。
又比如:
1 def test(x): 2 while True: 3 x *= 2 4 x = yield x 5 6 g = test(3) 7 print(next(g)) # 6 8 print(g.send(12)) # 24
第四行意思:先看右邊yield x 就是返回 x 。再看左邊 x = yield 就是賦值給 x 。所以不難理解了。不解釋了。
5. 補充(點選圖片檢視原文)