2013-08-27 39 views
1

我正在學習腳本語言python。我相當瞭解Java。我正在嘗試將一段代碼從Java轉換爲python。但他們的行爲不正常(或我的理解可能是完全錯誤的) 我在Java中有以下代碼,我無限期地向ArrayList添加元素。 所以這會導致內存不足的錯誤,這是我想到:爲什麼列表在Python和Java中表現不同?

import java.util.*; 
public class Testing{ 
public static void main(String[] args){ 
     ArrayList<Integer> list = new ArrayList<Integer>(); 
     list.add(5); 
     list.add(4); 
     for (int i=0;i<list.size();i++){ 
      list.add(5); 
     } 
} 
} 

的翻譯有蟒蛇同一代碼:

lst = [] 
lst.append(5) 
lst.append(4) 
for i in range(len(lst)): 
    lst.append(5) 
print lst 

在這裏,我得到的輸出:[5, 4, 5, 5]

從我所看到的,是該列表沒有通過作爲參考在Python中的for循環?

同樣在這裏,

>>> l=[1,2,3] 
>>> for i in l[:]: 
... l.append(4) 
... print l 
... 
[1, 2, 3, 4] 
[1, 2, 3, 4, 4] 
[1, 2, 3, 4, 4, 4] 
for循環內每個迭代

,我增加了列表的大小,所以迭代應該永遠正確的嗎?

+0

python是否只檢查一次大小? Java在每個循環迭代中評估「for」條件。 –

+8

爲什麼Python會永遠循環? 'range()'結果計算**一次**,而不是每次循環迭代。 –

回答

10

一個Python被評估for循環計算能產生可迭代的表達,其中通過所述環一次只。您可以操作循環中的lst對象,而不會影響for循環的結果。這不同於Java for構造(這是與Python for聲明非常不同的構造,其實際上是Foreach構造),其評估每個迭代的3個關聯表達式。

在第一個示例中,您創建了range()結果,一旦創建該結果,就不會更新每個循環迭代的結果。

在第二個示例中,您使用全長片段(lst[:])爲循環迭代創建了lst的副本。不會爲每個循環迭代重新創建副本。

然而,這裏有一個警告。 for循環在要迭代的對象上調用iter()。對於列表對象,結果列表迭代器不會保留對原始列表的引用以及迭代索引。每當for循環前進迭代器(在其上調用next())時,迭代索引就會遞增並在原始列表中查找,直到索引等於列表長度。如果不斷添加到循環中的列表中,那麼會創建一個無限循環

你可以看到這一點,如果你做創建列表的副本來遍歷:

>>> L = [1, 2, 3] 
>>> for i in L: 
...  L.append(4) 
...  print L 
...  if len(L) > 30: 
...   break 
... 
[1, 2, 3, 4] 
[1, 2, 3, 4, 4] 
[1, 2, 3, 4, 4, 4] 
[1, 2, 3, 4, 4, 4, 4] 
[1, 2, 3, 4, 4, 4, 4, 4] 
[1, 2, 3, 4, 4, 4, 4, 4, 4] 
[1, 2, 3, 4, 4, 4, 4, 4, 4, 4] 
[1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4] 
[1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4] 
[1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4] 
[1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4] 
[1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4] 
[1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4] 
[1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4] 
[1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4] 
[1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4] 
[1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4] 
[1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4] 
[1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4] 
[1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4] 
[1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4] 
[1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4] 
[1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4] 
[1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4] 
[1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4] 
[1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4] 
[1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4] 
[1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4] 

這裏,L創建的迭代器不斷屈服爲L下一個元素時延長循環,如果我沒有在循環中添加長度限制,這將永遠持續下去。

+0

謝謝!這就說得通了。在Java中,正如其他提到的那樣,list.size是針對每次迭代計算的。我認爲在python中,len(lst)是爲每次迭代計算的,範圍由新len決定。現在我明白了迭代器只能被評估一次 – eagertoLearn

+0

該警告可能有點令人困惑,因爲它不適用於他的任何一個示例。對於那些已經瞭解它的人來說,這顯然意味着什麼,但對於OP來說,它可能有助於展示您實際上是在何時迭代列表本身。 (否則,很好的答案。) – abarnert

6

range(len(lst))創建範圍一次,然後遍歷的是,儘管在Java list.size()在每次迭代

1

通過將for循環轉換爲等效的while循環來解釋此問題可能最容易。

在Java:

for (int i=0;i<list.size();i++){ 
    list.add(5); 
} 

int i=0; 
while (i<list.size()) { 
    list.add(5); 
    ++i; 
} 

在Python僞代碼:

for i in range(len(lst)): 
    lst.append(5) 

_r = range(len(lst)) 
_i = iter(r) 
while _i isn't done: 
    next(_i) 
    lst.append(5) 

實際上,你並不真的需要了解如何iter *和next工作,還是細節如何「 _i未完成「部分作品**;關鍵是for循環會創建一個迭代器,然後遍歷它。在你的情況下,它創建了一個對象range的迭代器(或者,在Python 2.x中,由range函數返回的list)。

但即使不知道,您可以看到您的len(lst)僅在開始時評估過一次,用於創建迭代器,而每次通過循環評估等效的Java list.size()


如果你想要一個Java風格for迴路的等效,你必須明確地寫出while循環。


* iter創建在任何迭代的迭代器(列表,一個範圍內,甚至是另一個迭代)。迭代器就像一個智能對象,它有一個對迭代器的引用和一個「當前位置」,儘管在封面下他們很少以這種方式實現。在迭代器上調用next可以有效地返回當前位置的值,並將迭代器前進到下一個迭代器(或者實際上實現迭代器的等效項)。

**什麼實際發生的是,實際上,一個try:/except StopIteration:,因爲呼籲這樣做了一個迭代next提高StopIteration。當然,它是用C語言(或Java或.NET或RPython,用於其他Python實現)實現的,而C實際上使用了一些特殊的魔法代碼來循環迭代器,使其更快一些,但幾乎沒有人需要考慮那部分。

2

>是不是通過引用到python for循環的列表?

所有對象都通過Python中的引用傳遞,而在Python 中,所有對象都是。 (Java原始值都沒有,但即使是普通的整數和浮點值在Python對象。)

在每次迭代裏面for循環>,我增加了列表的大小,所以迭代應該永遠正確的嗎?

您正在增加l的大小,這是正確的,但l [:]只被計算一次並生成l的淺表副本。該副本在循環中沒有更改,因此對循環內部l的更改不會更改循環變量將採用的值集。

在該循環中將l [:]更改爲l,然後您會看到很多輸出。

+0

謝謝。使總體感覺! – eagertoLearn

相關問題