python筆記十(列表生成式、字典生成式、生成器、生成器的並行)


一、列表生成式

  列表生成式就是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過來的一個值。

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM