本文中所有代碼均運(yùn)行在Python 2.7上
從迭代器說起
迭代器(Iterator),顧名思義,就是一個可供迭代(Iterables)的對象。
比如,在一個列表中,依次讀取其中的元素,就是一個迭代的過程。簡單來說,可以使用for ... in ...語句迭代訪問的對象,都是迭代器。其中包括list, set, string, dict , file等等。
這種簡潔的迭代方式非常簡便易用,但同時也帶來了一個問題,因?yàn)榈髦兴械臄?shù)據(jù)都被放在內(nèi)存中以供使用,所以會對內(nèi)存造成很大的壓力。
舉個栗子:
>>> for num in range(10**1000):
string = ' ' + ' '
....
OverflowError: range() result has too many items
也許在大多數(shù)場景中,我們不會去主動使用range()進(jìn)行如此大的一個迭代,但我們很有可能會通過open()去打開一個大文件(可能是一個比較大的原始數(shù)據(jù)文件,或者一個大的log文件等),它的大小很有可能會大于運(yùn)行環(huán)境的內(nèi)存,這時候程序就會因?yàn)?code>OverflowError而崩潰退出。
試試生成器?
同樣的,生成器具備迭代器的所有特性,但是它僅供迭代一次,因?yàn)樗捎玫?a target="_blank" rel="nofollow">惰性計(jì)算的優(yōu)化策略,就是說它只有當(dāng)被使用到的時候才把數(shù)據(jù)取出或者計(jì)算出,放到內(nèi)存中,訪問之后隨機(jī)銷毀。
在使用上,它和迭代器的主要區(qū)別在于僅能進(jìn)行一次迭代訪問。
>>> iterator = [x for x in range(7)]
>>> Iterator
[0, 1, 2, 3, 4, 5, 6]
>>> generator = (x for x in range(7))
( generator object (genexpr) at 0X00000000025DBA20)
總結(jié)
在多數(shù)情況下,我們操作的數(shù)據(jù)量都不足以撼動內(nèi)存,那是否意味著迭代對我們已經(jīng)完全夠用,生成器完全無用呢?
非也,惰性求值不僅在內(nèi)存層級對空間有著優(yōu)化的效果,在計(jì)算時間上也有著一定的提高。由于避免了不必要的計(jì)算,節(jié)省了計(jì)算資源。
最后以一個迭代器和生成器之間的斐波那契函數(shù)對比結(jié)束。
#先來一個迭代版本
def fib(n):
a, b = 0, 1
if n<2:
return [a, b][n]
for i in range():
a, b = b, a+b
return b
#之后是生成器版本
def fibHelper():
a, b = 0, 1
while True:
yield a
a, b = b, a+b
def fib(n):
while n>0:
result = fibHelper()
n -= 1
return result