函數作為返回值
高階函數除了可以接收函數作為參數外,還可以把函數作為結果值返回。
def lazy_sum(*args): def sum(): ax=0 for n in args: ax = ax + n return ax return sum f = lazy_sum(1,2,3,4,5) print f # <function sum at 0x02657770> # lazy_sum(1,2,3,4,5)返回的是一個指向求和的函數的函數名。 # 在調用lazy_sum(1,2,3,4,5)的時候,不立刻求和,而是根據后面代碼的需要在計算。 print f() # 15 # 用f()調用求和函數,計算出結果。 f1 = lazy_sum(1,2,3,4,5,6) f2 = lazy_sum(1,2,3,4,5,6) print f1 == f2 # False # lazy_sum()每調用一次,都會返回一個獨一無二的函數地址。
例中,lazy_sum中的內部函數sum引用了外部函數lazy_sum的參數和局部變量,
當lazy_sum返回函數sum時,相關參數和變量已經保存在返回的函數sum中了。
我們稱這為 閉包。
若要返回一個列表,列表每個元素都是函數。要注意閉包中局部變量的使用
def count(): fs = [] for i in range(1,4): def f(): return i*i fs.append(f) return fs f1, f2, f3 = count() print f1() print f2() print f3() # 9 # 9 # 9
結果全部都是9. 不是預期的1,4,9。
原因是返回函數引用了變量i,下面來解析一下f1,f2,f3=count()這句的執行過程:
當i=1, 執行for循環, 結果返回函數f的函數地址,存在列表fs中的第一個位置上。
當i=2, 由於fs列表中第一個元素所指的函數中的i是count函數的局部變量,i也指向了2;然后執行for循環, 結果返回函數f的函數地址,存在列表fs中的第二個位置上。
當i=3, 同理,在fs列表第一個和第二個元素所指的函數中的i變量指向了3; 然后執行for循環, 結果返回函數f的函數地址,存在列表fs中的第三個位置上。
所以在調用f1()的時候,函數中的i是指向3的:
f1():
return 3*3
同理f2(), f3()結果都為9
閉包時牢記的一點就是:返回函數不要引用任何循環變量,或者后續會發生變化的變量。即包在里面的函數(本例為f()),不要引用外部函數(本例為count())的任何循環變量
如果一定要引用循環變量怎么辦?方法是再創建一個函數,用該函數的參數綁定循環變量當前的值,無論該循環變量后續如何更改,已綁定到函數參數的值不變:
def count(): fs = [] for i in range(1,4): def f(j): def g(): return j*j return g fs.append(f(i)) return fs f1, f2, f3 =count() print f1() print f2() print f3()
結果就是預期的1,4,9.
當i=1時,f(1)即讓j指向1,
當i=2時,f(2)即讓j指向2,此時j不是count的局部變量,不會影響到i=1是f(1)中j的指向。即函數f的參數綁定循環變量當前的值, 而不是循環變量本身。
匿名函數
print map(lambda x: x*x, [1,2,3,4,5,6]) #[1, 4, 9, 16, 25, 36]
匿名函數,沒有函數名,不必擔心函數名沖突。
匿名函數有一個限制,就是只能有一個表達式,不用寫return,返回值就是該表達式的結果。
匿名函數也是一個函數對象,也可以把匿名函數。
f = lambda x: x*x print f print f(5) # <function <lambda> at 0x025277F0> # 25
同樣,也可以把匿名函數同為返回值返回。
def build(x,y): return lambda: x*x +y*y f = build(1,5) print f print f() # <function <lambda> at 0x025377F0> # 26
