我會嘗試更多的解釋深入。
如果你
i = 0
f = lambda: i
創建函數(拉姆達本質上是一個函數),它訪問其封閉範圍的i
變量。
在內部,它通過具有包含i
的所謂閉合來實現。鬆散地說,它是一種指向實際變量的指針,它可以在不同的時間點保存不同的值。
def a():
# first, yield a function to access i
yield lambda: i
# now, set i to different values successively
for i in range(100): yield
g = a() # create generator
f = next(g) # get the function
f() # -> error as i is not set yet
next(g)
f() # -> 0
next(g)
f() # -> 1
# and so on
f.func_closure # -> an object stemming from the local scope of a()
f.func_closure[0].cell_contents # -> the current value of this variable
這裏,所有i
的值都是 - 在他們的時間 - 存儲在那個閉包中。如果功能f()
需要它們。它從那裏獲得它們。
你可以看到在反彙編列表這種差異:
這些說功能a()
和f()
拆卸這樣的:
>>> dis.dis(a)
2 0 LOAD_CLOSURE 0 (i)
3 BUILD_TUPLE 1
6 LOAD_CONST 1 (<code object <lambda> at 0xb72ea650, file "<stdin>", line 2>)
9 MAKE_CLOSURE 0
12 YIELD_VALUE
13 POP_TOP
3 14 SETUP_LOOP 25 (to 42)
17 LOAD_GLOBAL 0 (range)
20 LOAD_CONST 2 (100)
23 CALL_FUNCTION 1
26 GET_ITER
>> 27 FOR_ITER 11 (to 41)
30 STORE_DEREF 0 (i)
33 LOAD_CONST 0 (None)
36 YIELD_VALUE
37 POP_TOP
38 JUMP_ABSOLUTE 27
>> 41 POP_BLOCK
>> 42 LOAD_CONST 0 (None)
45 RETURN_VALUE
>>> dis.dis(f)
2 0 LOAD_DEREF 0 (i)
3 RETURN_VALUE
與此相比,功能b()
看起來像
>>> def b():
... for i in range(100): yield
>>> dis.dis(b)
2 0 SETUP_LOOP 25 (to 28)
3 LOAD_GLOBAL 0 (range)
6 LOAD_CONST 1 (100)
9 CALL_FUNCTION 1
12 GET_ITER
>> 13 FOR_ITER 11 (to 27)
16 STORE_FAST 0 (i)
19 LOAD_CONST 0 (None)
22 YIELD_VALUE
23 POP_TOP
24 JUMP_ABSOLUTE 13
>> 27 POP_BLOCK
>> 28 LOAD_CONST 0 (None)
31 RETURN_VALUE
循環的主要區別是
在
b()
與
>> 27 FOR_ITER 11 (to 41)
30 STORE_DEREF 0 (i)
在
a()
>> 13 FOR_ITER 11 (to 27)
16 STORE_FAST 0 (i)
:在STORE_DEREF
存儲在cell
對象(關閉),而STORE_FAST
採用的是 「正常」 的變量,它(可能)的工作原理有點快。
拉姆達也有差別:
>>> dis.dis(lambda: i)
1 0 LOAD_GLOBAL 0 (i)
3 RETURN_VALUE
這裏有一個LOAD_GLOBAL
,而上面的一個採用LOAD_DEREF
。後者也是關閉的。
我完全忘了lambda i=i: i
。
如果你有值作爲默認參數,它發現它的方式爲通過完全不同的道路功能:
>>> i = 42
>>> f = lambda i=i: i
>>> dis.dis(f)
1 0 LOAD_FAST 0 (i)
3 RETURN_VALUE
:的
i
當前值被通過默認的參數傳遞給剛創建功能
這種方式的功能被稱爲f()
。它檢測到缺少參數並用缺省值填充相應的參數。所有這些都在函數被調用之前發生;從函數內部你可以看到這個值被取回並返回。
還有另外一種方法可以完成你的任務:只需使用lambda就好像它會取值:lambda i: i
。如果你打電話給它,它會抱怨缺少一個參數。
但你可以應付,隨着使用functools.partial
:
ff = [functools.partial(lambda i: i, x) for x in range(100)]
ff[12]()
ff[54]()
此包裝得到了調用和一些參數傳遞。結果對象是一個可調用的對象,它用這些參數以及你給它的任何參數調用原始的可調用對象。它可以在這裏用來鎖定預期的價值。
你還需要什麼解釋?您所接受問題的接受答案可以非常簡潔地解釋這種差異。 – BrenBarn
事實上,我想了解爲什麼參數值可以更新或不取決於編碼風格。 – Plouff