惰性求值
惰性求值(Lazy evaluation)是在須要時才進行求值的計算方式。表達式不在它被綁定到變量以後就當即求值,而是在該值被取用的時候求值。
除能夠獲得性能的提高(更小的內存佔用)外,惰性計算的最重要的好處是它能夠構造一個無限的數據類型。
yield的概念
yield的功能相似於return,可是不一樣之處在於它返回的是生成器。
生成器
生成器是經過一個或多個yield表達式構成的函數,每個生成器都是一個迭代器(可是迭代器不必定是生成器)。
若是一個函數包含yield關鍵字,這個函數就會變爲一個生成器。
生成器並不會一次返回全部結果,而是每次遇到yield關鍵字後返回相應結果,並保留函數當前的運行狀態,等待下一次的調用。
因爲生成器也是一個迭代器,那麼它就應該支持next方法來獲取下一個值。
# coding=utf8
# author=AaronChou
# 經過`yield`來建立生成器
def func():
for i in xrange(10):
yield i
# 經過列表來建立生成器
[i for i in xrange(10)]
# 調用以下
f = func()
print f # 此時生成器尚未運行
# <generator object func at 0x7fe01a853820>
print f.next() # 當i=0時,遇到yield關鍵字,直接返回
# 0
print f.next() # 繼續上一次執行的位置,進入下一層循環
# 1
# ...
print f.next()
# 9
print f.next() # 當執行完最後一次循環後,結束yield語句,生成StopIteration異常
# Traceback (most recent call last):
# File "<stdin>", line 1, in <module>
# StopIteration
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
當函數執行結束的時候,generator自動自動拋出StopIteration的異常,表示迭代的結束,而在for循環中,咱們不須要手動的進行處理異常,循環會自動的正常結束。
除了next函數,生成器還支持send函數。該函數能夠向生成器傳遞參數。
# 經過`yield`來建立生成器
def func():
n = 0
while 1:
n = yield n # 能夠經過send函數向n賦值
f = func()
print f.next() # 默認狀況下n爲0
# 0
print f.send(1) # n賦值1
# 1
print f.send(6)
# 6
1
2
3
4
5
6
7
8
9
10
11
12
13
14
注意fac不可迭代,而fac(5)可迭代
一個帶有yield的函數就是一盒generator,它和普通的函數不一樣,聲稱一個generator看起來想函數調用,可是部執行任何函數代碼,直到對其調用next()(注意在for循環中會自動調用next)纔開始執行。雖然執行流程和普通函數同樣,可是每執行到一個yield語句,就會中斷,並返回一個迭代值,下次執行的時候從yield的下一個語句開始執行。看起來像是一個函數在正常執行的過程當中被yield中斷了數次,每次中斷都會經過yield返回當前迭代器的值。
yield的好處顯而易見,把一個函數該寫成generator就得到了迭代能力,比起在類的實例中保存狀態計算下一個next的值,更加使代碼清潔,並且執行流程很是清晰
判斷是否爲generator
方法是使用isgeneratorfunction來進行判斷
from inspect import isgeneratorfunction
isgeneratorfunction(fac)
1
2
應用
最經典的例子,生成無限序列。
常規的解決方法是,生成一個知足要求的很大的列表,這個列表須要保存在內存中,很明顯內存限制了這個問題。
def get_primes(start):
for element in magical_infinite_range(start):
if is_prime(element):
return element
1
2
3
4
使用生成器就不須要返回整個列表,每次都只是返回一個數據,避免了內存的限制問題。
def get_primes(number):
while True:
if is_prime(number):
yield number
number += 1
1
2
3
4
5
用生成器生成一個Fibonacci數列:
def fab(max):
a, b = 0, 1
while a < max:
yield a
a, b = b, a + b
for i in fab(20):
print i, ",",
1
2
3
4
5
6
7
8
9
10
迭代器(iterator),生成器(constructor),Yield
當建立了一個列表,能夠一個個的讀取它的每一項,這叫作迭代器(iterator)。能夠用在for...in...語句中的都是可迭代的:好比lists,strings,files…由於這些可迭代的對象你能夠隨意的讀取因此很是方便易用,可是必須把它們的值放到內存裏,當它們有不少值時就會消耗太多的內存.
mylist = [x * x for x in range(3)]
for i in mylist:
print i
1
2
3
生成器(constructor)也是迭代器的一種,可是隻能迭代它們一次,緣由很簡單,由於它們不是所有存在內存裏,它們只在要調用的時候在內存裏生成。
mygenerator = (x * x for x in range(3))
for i in mygenerator:
print i
1
2
3
生成器和迭代器的區別就是用()代替[],不能用for i in mygenerator第二次調用生成器:首先計算0,而後會在內存裏丟掉0去計算1,直到計算完4.
mygenerator = (x * x for x in range(3))
for i in mygenerator:
yield i * i
1
2
3
使用Yield時,調用函數的時候,函數裏的代碼並無運行。函數僅僅返回生成器對象,而後,每當for語句迭代生成器的時候你的代碼纔會運轉。節省了內存空間。
Yield其實就是Python中應用了惰性求值的思想,使得函數可以創建可計算的無限列表而沒有妨礙計算的無限循環或大小問題
參考:
http://www.ibm.com/developerworks/cn/opensource/os-cn-python-yield/
http://www.cnblogs.com/coder2012/p/4990834.html