一、列表生成式
列表生成式就是python設置的可以用來可以生成列表的。
如要生成一個0-9的列表我們可以通過以下代碼實現:
>>> list(range(10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
但是如果生成的列表較為復雜呢?例如生成包含0²、1²、2²。。。9²這樣一個列表;
>>> L = [] >>> for i in range(10): ... L.append(i*i) ... >>> L [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
在上述代碼中,我們通過for循環將數值append到列表L中,雖然可以實現,但是也是low爆了~~~,以下通過一行代碼搞定!!!
>>> [i*i for i in range(10)] [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
除此之外,列表生成式還可以生成更為復雜的列表。通過列表生成式可以快速生成格式化的列表、字典
>>> d ={"name":"nadech","age":"22","address":"NANJING"} >>> [key+"="+value for key,value in d.items()] ['name=nadech', 'age=22', 'address=NANJING']
>>> from numpy.random import randn
>>> data ={i:randn() for i in range(7)}
>>> data
{0: 0.05824826050892203, 1: 0.08046687699730207, 2: 1.860740808203487, 3: 1.577136929714018, 4: -0.5473223742129686, 5: 0.13849329354272613, 6: 1.4133333866268165}
二、生成器
通過列表生成式,我們可以直接創建一個列表的所有元素。
但是,受到內存限制,列表容量肯定是有限的。而且,創建一個包含100萬個元素的列表,不僅占用很大的存儲空間,如果我們僅僅需要訪問前面幾個元素,那后面絕大多數元素占用的空間都白白浪費了。
如果列表可以按照需求,一邊循環一邊計算,就可以解決上述問題。這種機制就叫做生成器(generator)。
生成器共有兩種形式,第一種就是把列表生成式中的[ ]改為( );第二種就是含有yield
>>> g = (i*i for i in range(1,3)) >>> next(g) 1 >>> next(g) 4 >>> next(g) Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration
>>> (i*i for i in range(10))
<generator object <genexpr> at 0x0000029EA41490F8>
>>> for i in g:
... print(i)
...
1
4
上述代碼加粗部分可以看出,創建生成器返回的是一個生成器對象的地址,並不是直接包含所有的元素的列表。
通過調用next,可以生成下個元素的值,不過在實際使用中我們並不會通過多次調用next,而是通過for循環來獲取生成器的元素。
第二種我們要介紹的就是包含yield的,首先我們先實現打印輸出這樣一串數字,除了第一個數,后一個數都等於前兩個數字的和 1,1,2,3,5,8....這就是著名的斐波那契數列,不要被這名字嚇到,總之就是實現輸出上邊的一串數字。
>>> 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(5) 1 1 2 3 5 'done'
接下來,我們對上述代碼進行一處修改,把print改為yield
>>> def fib(max): ... n,a,b = 0,0,1 ... while n<max: ... yield(b) ... a,b = b,a+b ... n=n+1 ... return "done" ... >>> fib(5) <generator object fib at 0x000001F8B9C2AF68>
>>> o = fib(6)
>>> for i in o:
... print (i)
...
1
1
2
3
5
8
可見,命令窗口返回一個generator對象的內存地址。通過迭代輸出生成器 o中的數字。
但是,細心的朋友可能發現,我們通過for循環來輸出生成器的內容的時候,並沒有輸出return的“done”,這樣我們就需要通過next來依次輸出,同時就伴隨另外一個問題的產生,當next輸出到最后的時候,會拋出一個StopIteration的異常,我們需要將次異常捕獲。(異常的捕獲后邊會詳細介紹)
>>> while True: ... try: ... x = next(o) ... print("o:",x) ... except StopIteration as e: ... print("RETURN VALUE:", e.value) ... break ... o: 1 o: 1 o: 2 o: 3 o: 5 o: 8 RETURN VALUE: done
三、生成器的並行
搞清楚了生成器的特點,那么我們簡單的利用生成器來實現一個並行的效果,實際上這是一個假的並行。
這個生成器並行的例子,叫做大豬和小豬吃包子。
import time def consumer(name): print("%s 准備好吃包子了" % name) while True: type = yield #此處的yield,可以接受producer中send的傳值 print("%s包子被%s 吃了" % (type, name)) def producer(name): print("%s 已經准備好做包子了~~~~"%name) c = consumer("大豬") #這時候不是正常的函數調用了,只是生成一個生成器隊形,不執行函數體內容,調用next會執行 c1 = consumer("小豬") next(c) next(c1) items = ["白菜","菠菜","生菜"] for item in items : time.sleep(1) print("%s已經做好了兩個包子"%name) c.send(item) c1.send(item) producer("nadech")
以上代碼中,我們先定義了消費者函數,這個函數就用來描述大豬和小豬吃包子,我們可以看到該函數包含有yield,實際上它是一個生成器。與上個例子中的斐波那契函數不同的是這里的yield,可以接受該生成器send過來的一個值。