2017-01-15 75 views
4

可能有人請幫助我理解爲什麼它實現了「埃拉托色尼篩」下面的代碼不同的Python 2和Python 3的Python 2比Python的3 - 過濾器的行爲差異

l = range(2, 20) 
for i in range(2, 6): 
    l = filter(lambda x: x == i or x % i != 0, l) 
print(tuple(l)) 

表現不同使用Python 2.7:

> python filter.py 
(2, 3, 5, 7, 11, 13, 17, 19) 

與Python 3.6:

> python filter.py 
(2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 16, 17, 18, 19) 

我明白Python3的過濾器返回一個過濾器對象,但無法解釋最終結果。 (代碼來自這個lambdas教程1)。

+4

因爲在Python的3.x中,'過濾器用作發電機。 –

+2

@WillemVanOnsem你應該寫一個完整的答案。這種問題值得。 – Soviut

+2

@Soviut:我正在努力。 –

回答

3

有兩個部分,在這裏發揮作用:

  • filter作品作爲發電機:過濾完成懶惰;
  • ilambda x : ...更新以及在for循環使進展。

所以在最後,你已經構建的是一樣的東西:

l = filter(lambda x: x == 5 or x % 5 != 0, 
     filter(lambda x: x == 5 or x % 5 != 0, 
      filter(lambda x: x == 5 or x % 5 != 0, 
       filter(lambda x: x == 5 or x % 5 != 0,l) 
      ) 
     ) 
    ) 

注意彷彿i5所有的時間所有過濾完成。所以現在你打電話給tuple(..),實際的過濾就完成了,正如你所看到的,只有5個不是五個自身的倍數被過濾掉。

一個簡單的辦法是使用list在循環使得filter ING正在積極做:

l = range(2, 20) 
for i in range(2, 6): 
    l = list(filter(lambda x: x == i or x % i != 0, l)) 
print(tuple(l)) 

在Python返回運行此:

>>> l = range(2, 20) 
>>> for i in range(2, 6): 
...  l = list(filter(lambda x: x == i or x % i != 0, l)) 
... 
>>> print(l) 
[2, 3, 5, 7, 11, 13, 17, 19] 

記住,雖然外觀這些實際上是「不同的」彼此不兼容的語言:寫入一個的運行代碼不會總是在另一箇中運行,反之亦然。

另注(學分@ShadowRanger)是一個真正可以綁定在你的拉姆達i。你可以通過創建一個「更高階的lambda」來做到這一點。而是寫:

lambda x : x == i or x % i != 0 

你寫:

(lambda j : (lambda x : x == j or x % j != 0))(i) 

會發生什麼事是你定義一個函數,需要輸入一個j實際需要的i值。通過立即調用它,j綁定到值i

+1

請注意,通過在'lambda'中綁定'i'的值,可以在沒有中間列表的情況下得到相同的結果,因此它不會在每個循環中更新:'filter(lambda x,i = i:x == i or x%i!= 0,l)'設置'i = i'的默認參數在定義時綁定,所以它不會被循環的進程修改。 – ShadowRanger

+0

@ShadowRanger:這確實是一個很好的觀點。我將編輯我的答案以包含此內容。 –

+0

@ShadowRanger:我定義了一個更高階的函數,因爲它更清楚發生了什麼。儘管如此,你的方法也適用。這一切都與定義本地'i'有關。 –

7

在Python-3中filter返回一個生成器(在Python-2中它返回一個列表),所以在你使用它的時候評估它。但這本身並不是問題,問題在於你的i發生了變化。當你消耗filter你的i = 5和你所有的filter只是檢查。

我有一些print -statements讓您可以更輕鬆地跟隨所發生的事情:

l = range(2, 20) 
for i in range(2, 6): 
    l = filter(lambda x: print(x, i) or (x == i or x % i != 0), l) 
list(l) 

2 5 
2 5 
2 5 
2 5 
3 5 
3 5 
3 5 
3 5 
4 5 
4 5 
4 5 
4 5 
5 5 
5 5 
5 5 
5 5 
6 5 
6 5 
6 5 
6 5 
7 5 
7 5 
7 5 
7 5 
8 5 
8 5 
8 5 
8 5 
9 5 
9 5 
9 5 
9 5 
10 5 
11 5 
11 5 
11 5 
11 5 
12 5 
12 5 
12 5 
12 5 
13 5 
13 5 
13 5 
13 5 
14 5 
14 5 
14 5 
14 5 
15 5 
16 5 
16 5 
16 5 
16 5 
17 5 
17 5 
17 5 
17 5 
18 5 
18 5 
18 5 
18 5 
19 5 
19 5 
19 5 
19 5 

[2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 16, 17, 18, 19] 

這可能不是你的意圖。你可以綁定i您拉姆達:

l = range(2, 20) 
for i in range(2, 6): 
    l = filter((lambda j: lambda x: print(x, j) or (x == j or x % j != 0))(i), l) 
    # or 
    # l = filter(lambda x, i=i: print(x, i) or (x == i or x % i != 0), l) 
list(l) 
2 2 
2 3 
2 4 
2 5 
3 2 
3 3 
3 4 
3 5 
4 2 
5 2 
5 3 
5 4 
5 5 
6 2 
7 2 
7 3 
7 4 
7 5 
8 2 
9 2 
9 3 
10 2 
11 2 
11 3 
11 4 
11 5 
12 2 
13 2 
13 3 
13 4 
13 5 
14 2 
15 2 
15 3 
16 2 
17 2 
17 3 
17 4 
17 5 
18 2 
19 2 
19 3 
19 4 
19 5 

[2, 3, 5, 7, 11, 13, 17, 19] 

或投你filter -result立刻到tuple

l = range(2, 20) 
for i in range(2, 6): 
    l = tuple(filter(lambda x: x == i or x % i != 0, l)) 
print(l) 
# (2, 3, 5, 7, 11, 13, 17, 19)