標籤:last lis 做了 while 方式 執行流程 for迴圈 代碼 font
一、列表產生式
什麼是列表產生式?
列表產生式是快速產生列表的一種方式。(貌似有些廢話)
更專業點的說法:列表產生式即List Comprehensions,是Python內建的非常簡單卻強大的可以用來建立list的產生式。
在python2.7裡
舉個例子,要產生list [1,2,3,4,5],可以用range(1,6)
>>> range(1,6)[1, 2, 3, 4, 5]
但是如果要產生[1x1,2x2,3x3,4x4,5x5]怎麼做呢?
普通青年做法:
>>> L = []>>> for x in range(1,6):... L.append(x*x)... >>> L[1, 4, 9, 16, 25]
但是這種方式,太繁瑣、也太LOW,用列表產生式一行代碼搞定:
>>> [ x*x for x in range(1,6) ][1, 4, 9, 16, 25]
在python3裡
>>> range(1,6)range(1, 6)>>> L = []>>> for x in range(1,6):... L.append(x*x)... >>> L[1, 4, 9, 16, 25]>>> [ x*x for x in range(1,6) ][1, 4, 9, 16, 25]
說明:
python2中的range返回的是一個列表
python3中的range返回的是一個迭代值
二、產生器
通過列表產生式,我們可以直接建立一個列表。但是,受到記憶體限制,列表容量肯定是有限的。而且,建立一個包含100萬個元素的列表,不僅佔用很大的儲存空間,如果我們僅僅需要訪問前面幾個元素,那後面絕大多數元素佔用的空間都白白浪費了。
所以,如果列表元素可以按照某種演算法推算出來,那我們是否可以在迴圈的過程中不斷推算出後續的元素呢?這樣就不必建立完整的list,從而節省大量的空間。在Python中,這種一邊迴圈一邊計算的機制,稱為產生器:generator。
如何建立產生器呢?
方法一:(把列表產生式的[]變為(),就建立了一個generator)
>>> L = [x * x for x in range(8)]>>> L[0, 1, 4, 9, 16, 25, 36, 49]>>> S = (x * x for x in range(8))>>> S<generator object <genexpr> at 0xb7102fcc>>>>
如何列印generator的每一個元素呢?
方法一:>>> next(S)0>>> next(S)1>>> next(S)4>>> next(S)9>>> next(S)16>>> next(S)25>>> next(S)36>>> next(S)49>>> next(S)Traceback (most recent call last): File "<stdin>", line 1, in <module>StopIteration>>> # 使用next方法來擷取下一回的傳回值,最終找不到更多的元素時,會拋出StopIteration錯誤。#更好的方式,推薦使用for迴圈來擷取元素值>>> S = (x * x for x in range(8))>>> for n in S:... print(n)... 014916253649>>>
方法二:函數(推算的演算法比較複雜,用類似列表產生式的for迴圈無法實現的時候,還可以用函數來實現)
最著名的例子:斐波拉契數列(Fibonacci),除第一個和第二個數外,任意一個數都可由前兩個數相加得到:
1, 1, 2, 3, 5, 8, 13, 21, 34, 55...
def fib(max): n,a,b = 0,0,1 while n < max: #print(b) yield b a,b = b,a+b n += 1 return "over"#一個函數定義中包含yield關鍵字,那麼這個函數就不再是一個普通函數,而是一個generator>>> f = fib(6)>>> f<generator object fib at 0x104feaaa0>
最難理解的就是generator和函數的執行流程不一樣。函數是順序執行,遇到return語句或者最後一行函數語句就返回。
而變成generator的函數,在每次調用next()的時候執行,遇到yield語句返回,再次執行時從上次返回的yield語句處繼續執行。
# next方法擷取值>>>data = fib(10)>>>print(data)>>>print(data.__next__())#更好的方式>>> for n in fib(6):... print(n)...112358# return 值只有在發生StopIteration錯誤時,才返回>>> list = fib(6)>>> while True:... try:... x = next(list)... print(‘list:‘, x)... except StopIteration as e:... print(‘return value:‘, e.value)... break...list: 1list: 1list: 2list: 3list: 5list: 8return value: over
擴充:
yield實現在單線程的情況下實現並發運算的效果
import timedef consumer(name): print("%s 準備吃包子啦!" % name) while True: baozi = yield print("包子[%s]來了,被[%s]吃了" % (baozi,name))#c = consumer("R")#c.__next__()#c.__next__()def producer(name): c = consumer(‘A‘) c2 = consumer("B") c.__next__() c2.__next__() print("老子開始準備做包子了") for i in range(10): time.sleep(1) print("做了2個包子") c.send(i) c2.send(i)producer("coco")
三、迭代器
可迭代對象:可作用於for迴圈的對象都是可迭代對象(Iterable)
迭代器:可以被next()函數調用並不斷返回下一個值的對象稱為迭代器(Iterator),它們表示一個惰性計算的序列
判斷是否是可迭代類型:
# 可迭代類型:list、tuple、dict、set、str、generator>>> from collections import Iterable>>> isinstance([],Iterable)True>>> isinstance([1,2,3],Iterable)True>>> isinstance({},Iterable)True>>> isinstance((),Iterable)True>>> isinstance((4,5,6),Iterable)True>>> isinstance({},Iterable)True>>> isinstance(‘coco‘,Iterable)True>>> isinstance((x*x for x in range(1,6)),Iterable)True>>> isinstance(200,Iterable)False>>>
判斷是否是迭代器:
# 產生器都是Iterator對象,但list、dict、str雖然是Iterable,卻不是Iterator>>> from collections import Iterator>>> isinstance((x for x in range(10)), Iterator)True>>> isinstance([1,2], Iterator)False>>> isinstance({}, Iterator)False>>> isinstance(‘test‘, Iterator)False# 但是可以把list、dict、str等Iterable變成Iterator可以使用iter()函數:>>> isinstance(iter([1,2]), Iterator)True>>> isinstance(iter(‘test‘), Iterator)True
說明:
python的for迴圈本質上就是通過不斷調用next()函數來實現的。
# for迴圈for x in [1, 3, 5,7]: pass# 使用next迴圈擷取值的等價寫法it = iter([1, 3, 5,7])while True: try: # 獲得下一個值: x = next(it) except StopIteration: # 遇到StopIteration就退出迴圈 break
python列表產生式&產生器&迭代器