yield是生成的意思,但是在python中則是作為生成器理解,生成器的用處主要可以迭代,這樣簡化了很多運算模型(還不是很了解是如何簡化的)。
yield是一個表達式,是有返回值的.
當一個函數中含有yield時,它不再是一個普通的函數,而是一個生成器.當該函數被調用時不會自動執行,而是暫停,見第一個例子:
例1:
1
2
3
4
5
6
7
8
9
10
|
>>> def mygenerator(): ... print 'start...' ... yield 5 ... >>> mygenerator() / / 在此處調用,并沒有打印出start...說明存在 yield 的函數沒有被運行,即暫停 <generator object mygenerator at 0xb762502c > >>> mygenerator(). next () / / 調用 next ()即可讓函數運行. start... 5 >>> |
如一個函數中出現多個yield則next()會停止在下一個yield前,見例2:
例2:
1
2
3
4
5
6
7
8
9
10
|
>>> def mygenerator(): ... print 'start...' ... yield 5 ... >>> mygenerator() / / 在此處調用,并沒有打印出start...說明存在 yield 的函數沒有被運行,即暫停 <generator object mygenerator at 0xb762502c > >>> mygenerator(). next () / / 調用 next ()即可讓函數運行. start... 5 >>> |
為什么yield 5會輸出5,yield 23會輸出23?
我們猜測可能是因為yield是表達式,存在返回值.
那么這是否可以認為yield 5的返回值一定是5嗎?實際上并不是這樣,這個與send函數存在一定的關系,這個函數實質上與next()是相似的,區別是send是傳遞yield表達式的值進去,而next不能傳遞特定的值,只能傳遞None進去,因此可以認為g.next()和g.send(None)是相同的。見例3:
例3:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
>>> def fun(): ... print 'start...' ... m = yield 5 ... print m ... print 'middle...' ... d = yield 12 ... print d ... print 'end...' ... >>> m = fun() / / 創建一個對象 >>> m. next () / / 會使函數執行到下一個 yield 前 start... 5 >>> m.send( 'message' ) / / 利用send()傳遞值 message / / send()傳遞進來的 middle... 12 >>> m. next () None / / 可見 next ()返回值為空 end... Traceback (most recent call last): File "<stdin>" , line 1 , in <module> StopIteration |
在multiprocess中的使用
python在處理數據的時候,memory-heavy 的數據往往會導致程序沒辦反運行或者運行期間服務器其他程序效率受到影響。這種情況往往會把數據集合變為通過genertor來遍歷。
但同時如我們所知,generoter看似只能被單進程消費,這樣效率很低。
generator 可以被pool.map消費。
看一下pool.py的源碼。
1
2
3
4
5
6
7
|
for i, task in enumerate (taskseq): ... try : put(task) except IOError: debug( 'could not put task on queue' ) break |
實際是先將generator全部消費掉放到queue中。然后通過map來并行。這樣是解決了使用map來并行。
但是依然沒有解決占用內存的問題。這里有兩步占用內存。
- 第一步是全部消費掉的generator。
- 第二步并行運算全部data。
解決第一個問題,通過部分消費generator來達到。
解決第二個問題,可以通過imap來達到.
示例代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
import multiprocessing as mp import itertools import time def g(): for el in xrange ( 50 ): print el yield el import os def f(x): time.sleep( 1 ) print str (os.getpid()) + " " + str (x) return x * x if __name__ = = '__main__' : pool = mp.Pool(processes = 4 ) # start 4 worker processes go = g() result = [] N = 11 while True : g2 = pool.imap(f, itertools.islice(go, N)) if g2: for i in g2: result.append(i) time.sleep( 1 ) else : break print (result) |
ps: 使用注意事項。在produce數據的時候,盡量少做操作,應為即使是map也是單線程的來消費數據。所以盡量把操作放到map中作。這樣才能更好的利用多進程提高效率。