列表生成式即List Comprehensions,是Python內(nèi)置的非常簡單卻強(qiáng)大的可以用來創(chuàng)建list的生成式赠制。
格式如下:
l1 = [x for x in range(10)]
print(l1)
結(jié)果如下:
通過列表生成式惯悠,我們可以直接創(chuàng)建一個列表躬络。但是蜡吧,受到內(nèi)存限制,列表容量肯定是有限的征绎。而且蹲姐,創(chuàng)建一個包含100萬個元素的列表,不僅占用很大的存儲空間人柿,如果我們僅僅需要訪問前面幾個元素柴墩,那后面絕大多數(shù)元素占用的空間都白白浪費(fèi)了。所以凫岖,如果列表元素可以按照某種算法推算出來拐邪,那我們是否可以在循環(huán)的過程中不斷推算出后續(xù)的元素呢?這樣就不必創(chuàng)建完整的list隘截,從而節(jié)省大量的空間扎阶。在Python中,這種一邊循環(huán)一邊計算的機(jī)制婶芭,稱為生成器:generator东臀。
創(chuàng)建 L 和 G 的區(qū)別僅在于最外層的 [ ] 和 ( ) , L 是一個列表犀农,而 G 是一個生成器惰赋。我們可以直接打印出L的每一個元素,但我們怎么打印出G的每一個元素呢呵哨?如果要一個一個打印出來赁濒,可以通過 next() 函數(shù)獲得生成器的下一個返回值:
下面采用生成器的方式創(chuàng)建列表:
ge = (x for x in range(4))
print(type(ge))
print(next(ge))
print(next(ge))
print(next(ge))
print(next(ge))
print(next(ge))
結(jié)果如下:
生成器保存的是算法,每次調(diào)用 next(G) 孟害,就計算出 G 的下一個元素的值拒炎,直到計算到最后一個元素,沒有更多的元素時挨务,拋出 StopIteration 的異常击你。當(dāng)然,這種不斷調(diào)用 next() 實(shí)在是太變態(tài)了谎柄,正確的方法是使用 for 循環(huán)丁侄,因?yàn)樯善饕彩强傻鷮ο蟆K猿祝覀儎?chuàng)建了一個生成器后鸿摇,基本上永遠(yuǎn)不會調(diào)用 next() ,而是通過 for 循環(huán)來迭代它劈猿,并且不需要關(guān)心 StopIteration 異常拙吉。
ge = (x for x in range(10))
print(type(ge))
for i in ge:
print(i,end=' ')
結(jié)果如下:
generator非常強(qiáng)大。如果推算的算法比較復(fù)雜糙臼,用類似列表生成式的 for 循環(huán)無法實(shí)現(xiàn)的時候庐镐,還可以用函數(shù)來實(shí)現(xiàn)。
通過之前的學(xué)習(xí)变逃,可以完成斐波那契數(shù)列的函數(shù)版必逆,代碼如下:
def fib(times):
n = 0
a, b = 0, 1
while n < times:
print(b, end=' ')
a, b = b, a + b
n += 1
return 'done'
fib(5)
仔細(xì)觀察,可以看出揽乱,fib函數(shù)實(shí)際上是定義了斐波拉契數(shù)列的推算規(guī)則名眉,可以從第一個元素開始,推算出后續(xù)任意的元素凰棉,這種邏輯其實(shí)非常類似generator损拢。
也就是說,上面的函數(shù)和generator僅一步之遙撒犀。要把fib函數(shù)變成generator福压,只需要把print(b)改為yield b就可以了:
print()
print('*' * 15)
def fib(times):
n = 0
a, b = 0, 1
while n < times:
yield b
a, b = b, a + b
n += 1
return 'over'
ret = fib(7)
for i in ret:
print(i, end=' ')
結(jié)果如下:
如果用next調(diào)用掏秩,遇到y(tǒng)ield就會停止,所以同樣為了避免StopIteration異常荆姆,采用for循環(huán)遍歷蒙幻。在采用遍歷的時候,發(fā)現(xiàn)拿不到generator的返回值’over’胆筒。想要拿到返回值邮破,必須捕獲StopIteration異常,返回值包含在StopIteration的value中仆救。
關(guān)于生成器中的send的用法抒和,看如下代碼:
def fun():
i = 0
while True:
i += 1
print('...begin...')
temp = yield i
print('temp:%s'%temp)
ret = fun()
print(next(ret))
print('*'*10)
print(next(ret))
print('*'*10)
print(ret.send('haha'))
結(jié)果如下:
使用next()模擬多任務(wù)實(shí)現(xiàn)方式之一:協(xié)程
def test1():
while True:
print('...1...')
yield None
def test2():
while True:
print('...2...')
yield None
t1 = test1()
t2 = test2()
i = 0
while i < 3:
t1.__next__()
t2.__next__()
i += 1
結(jié)果如下:
生成器是這樣一個函數(shù),它記住上一次返回時在函數(shù)體中的位置彤蔽。對生成器函數(shù)的第二次(或第 n 次)調(diào)用跳轉(zhuǎn)至該函數(shù)中間摧莽,而上次調(diào)用的所有局部變量都保持不變。生成器不僅“記住”了它數(shù)據(jù)狀態(tài)铆惑;生成器還“記住”了它在流控制構(gòu)造(在命令式編程中范嘱,這種構(gòu)造不只是數(shù)據(jù)值)中的位置。
生成器的特點(diǎn):
節(jié)約內(nèi)存员魏。迭代到下一次的調(diào)用時丑蛤,所使用的參數(shù)都是第一次所保留下的,即是說撕阎,在整個所有函數(shù)調(diào)用的參數(shù)都是第一次所調(diào)用時保留的受裹,而不是新創(chuàng)建的。