惰性求值和yield Python

2021-07-31 09:10:09 字數 3579 閱讀 4035

應用迭代器iterator生成器constructoryield參考

惰性求值(lazy evaluation)是在需要時才進行求值的計算方式。表示式不在它被繫結到變數之後就立即求值,而是在該值被取用的時候求值。

除可以得到效能的提公升(更小的記憶體占用)外,惰性計算的最重要的好處是它可以構造乙個無限的資料型別

yield的功能類似於return,但是不同之處在於它返回的是生成器

生成器是通過乙個或多個yield表示式構成的函式,每乙個生成器都是乙個迭代器(但是迭代器不一定是生成器)。

如果乙個函式包含yield關鍵字,這個函式就會變為乙個生成器。

生成器並不會一次返回所有結果,而是每次遇到yield關鍵字後返回相應結果,並保留函式當前的執行狀態,等待下一次的呼叫。

由於生成器也是乙個迭代器,那麼它就應該支援next方法來獲取下乙個值。

# coding=utf8

# author=aaronchou

# 通過`yield`來建立生成器

deffunc

():for i in xrange(10):

yield i

# 通過列表來建立生成器

[i for i in xrange(10)]

# 呼叫如下

f = func()

print f # 此時生成器還沒有執行

# print f.next() # 當i=0時,遇到yield關鍵字,直接返回

# 0print f.next() # 繼續上一次執行的位置,進入下一層迴圈

# 1# ...

print f.next()

# 9print f.next() # 當執行完最後一次迴圈後,結束yield語句,生成stopiteration異常

# traceback (most recent call last):

# file "", line 1, in

# stopiteration

當函式執行結束的時候,generator自動自動丟擲stopiteration的異常,表示迭代的結束,而在for迴圈中,我們不需要手動的進行處理異常,迴圈會自動的正常結束。

除了next函式,生成器還支援send函式。該函式可以向生成器傳遞引數。

# 通過`yield`來建立生成器

deffunc

(): n = 0

while

1: n = yield n # 可以通過send函式向n賦值

f = func()

print f.next() # 預設情況下n為0

# 0print f.send(1) # n賦值1

# 1print f.send(6)

# 6

注意fac不可迭代,而fac(5)可迭代

乙個帶有yield的函式就是一盒generator,它和普通的函式不同,聲稱乙個generator看起來想函式呼叫,但是部執行任何函式**,直到對其呼叫next()(注意在for迴圈中會自動呼叫next)才開始執行。雖然執行流程和普通函式一樣,但是每執行到乙個yield語句,就會中斷,並返回乙個迭代值,下次執行的時候從yield的下乙個語句開始執行。看起來像是乙個函式在正常執行的過程中被yield中斷了數次,每次中斷都會通過yield返回當前迭代器的值。

yield的好處顯而易見,把乙個函式該寫成generator就獲得了迭代能力,比起在類的例項中儲存狀態計算下乙個next的值,更加使**清潔,而且執行流程非常清晰

方法是使用isgeneratorfunction來進行判斷

from inspect import isgeneratorfunction

isgeneratorfunction(fac)

最經典的例子,生成無限序列。

常規的解決方法是,生成乙個滿足要求的很大的列表,這個列表需要儲存在記憶體中,很明顯記憶體限制了這個問題。

def

get_primes

(start):

for element in magical_infinite_range(start):

if is_prime(element):

return element

使用生成器就不需要返回整個列表,每次都只是返回乙個資料,避免了記憶體的限制問題。

def

get_primes

(number):

while

true:

if is_prime(number):

yield number

number += 1

用生成器生成乙個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, ",",

當建立了乙個列表,可以乙個個的讀取它的每一項,這叫做迭代器(iterator)。可以用在for...in...語句中的都是可迭代的:比如lists,strings,files…因為這些可迭代的物件你可以隨意的讀取所以非常方便易用,但是必須把它們的值放到記憶體裡,當它們有很多值時就會消耗太多的記憶體.

mylist = [x * x for x in range(3)]

for i in mylist:

print i

生成器(constructor)也是迭代器的一種,但是只能迭代它們一次,原因很簡單,因為它們不是全部存在記憶體裡,它們只在要呼叫的時候在記憶體裡生成。

mygenerator = (x * x for x in range(3))

for i in mygenerator:

print i

生成器和迭代器的區別就是用()代替,不能用for i in mygenerator第二次呼叫生成器:首先計算0,然後會在記憶體裡丟掉0去計算1,直到計算完4.

mygenerator = (x * x for x in range(3))

for i in mygenerator:

yield i * i

使用yield時,呼叫函式的時候,函式裡的**並沒有執行。函式僅僅返回生成器物件,然後,每當for語句迭代生成器的時候你的**才會運轉。節省了記憶體空間。

yield其實就是python中應用了惰性求值的思想,使得函式能夠建立可計算的無限列表而沒有妨礙計算的無限迴圈或大小問題

惰性程式設計和惰性求值

惰性程式設計是一種將對函式或請求的處理延遲到真正需要結果時進行的通用概念。有很多應用程式都採用了這種概念,有的非常明顯,有些則不太明顯。從惰性程式設計的角度來思考問題,可以幫您消除 中不必要的計算,也可以幫您重構程式以使之更加面向問題。scheme 中的簡單惰性程式設計 惰性程式設計是這樣一種技術 ...

Mathematica 關於惰性求值

mathematica中,預設情況下是非惰性的,例如 x 2 f x 他實際計算的時候並不是先計算f x 然後把x 2代入,而是先計算引數,發現x 2,然後直接計算f 2 如何改變這一情況呢,有兩種方法 1.改變函式特性 例如setattributes f,holdall 這裡的holdall就可以...

柯里化與惰性求值

柯里化是函式式程式設計裡的概念。理論上說,柯里化是把接受多個引數的函式變換成接受乙個單一引數 最初函式的第乙個引數 的函式,並且返回接受餘下的引數而且返回結果的新函式的技術。例如有函式 sub f1 這裡f1接受2個引數。所以如果我們固定 b 1,就得到sub f1 1 這是乙個接受1個引數的新函式...