2013-10-04 40 views
39

經常在SO上的python問題中找到這種類型的表達式。無論是對剛剛訪問的迭代是否需要範圍(len(a))?

for i in range(len(a)): 
    print(a[i]) 

所有項目這是寫的只是一個clumbersome方式:

for e in a: 
    print(e) 

或者分配到可迭代的元素:

for i in range(len(a)): 
    a[i] = a[i] * 2 

應與以下相同:

for i, e in enumerate(a): 
    a[i] = e * 2 
# Or if it isn't too expensive to create a new iterable 
a = [e * 2 for e in a] 

或者超過指數過濾:

for i in range(len(a)): 
    if i % 2 == 1: continue 
    print(a[i]) 

這可能是這樣表示:

for e in a [::2]: 
    print(e) 

或者當你只需要在列表的長度,而不是它的內容:

for _ in range(len(a)): 
    doSomethingUnrelatedToA() 

其中可能是:

for _ in a: 
    doSomethingUnrelatedToA() 

在Python中我們已經enumerate,切片,filtersorted,等等...至於蟒蛇for結構旨在遍歷iterables不僅整數的範圍,是在你需要in range(len(a))有現實世界的使用情況?

+0

我認爲'範圍(len(a))'通常是對Python相當缺乏經驗的人(儘管不一定與編程有關)。 – rlms

+0

我在學習Python時只使用了'range(len(a))'。現在,我不這樣做,正如你所說的那樣,替換很容易。 – iCodez

+0

不是真的。我經常使用'range(len(a))',因爲我不需要列表a的內容,而只需要長度。 – aIKid

回答

4

簡短回答:從數學角度講,不是,實際上,是的,例如對於故意編程。

從技術上講,我認爲數學上正確的答案應該是「不,這是沒有必要的」,因爲它使用其他結構,即它等同於其他結構的表達......東西一樣,如果一個語言是圖靈完成,這不是無論它具有哪種語法/範式構造都無關緊要,因爲無論如何,所有東西都可以表達出來。

但是在實踐中,我使用for i in range(len(a)(或for _ in range(len(a)),如果我不需要索引)明確表示我想多次迭代序列中的項目而無需使用項目中的項目任何東西的序列。

那麼回答「有需要嗎?」 part -I 需要它爲了可讀性目的表示代碼的含義/意圖。

參見:https://en.wikipedia.org/wiki/Intentional_programming

附:但在同一時間,下面好像是語義上等同,從故意編程觀點:

for _ in a: 
    ... 

b = ["hello" for _ in a] 

......一切的一切,我猜不同之處在於你是否真的需要明確約「重複多次,因爲在a「而不是a中的每個元素都是a,不管的內容如何「 ...所以只是一個故意編程的細微差別。

+0

'_範圍內(len(a))''over'for _ in a'有什麼優勢? – Hyperboreus

+0

@Hyperboreus:是的,我剛剛在你的評論前幾​​秒鐘修改了我的答案......所以我猜想不同之處在於你是否想明確**「重複多次,因爲有'a'中的項目」 **,而不是「a」中的每個元素,而不管「a」的內容是什麼**「......所以只是一個故意編程的細微差別。 –

+0

謝謝你的例子。我把它列入我的問題。 – Hyperboreus

8

如果你需要一個序列的索引工作,然後是 - 你用它...例如用於numpy.argsort相當於...:

>>> a = [6, 3, 1, 2, 5, 4] 
>>> sorted(range(len(a)), key=a.__getitem__) 
[2, 3, 1, 5, 4, 0] 
+0

好的,這看起來很合理。非常感謝你。但問題是:你將如何處理新排序的索引列表。如果通過這個列表再次訪問一些迭代,狗咬自己的尾巴。 – Hyperboreus

+0

相當於:雖然你可以說是更好/更快的,但[ix for ix,_ in sorted(enumerate(a),key = lambda i:i [1])]'。 –

2

走向所作的評論以及個人經驗,我說不,需要range(len(a))。你可以用range(len(a))做的所有事情都可以通過另一種方式來完成(通常效率更高)。

你在你的文章中給出了很多例子,所以我在這裏不再重複。相反,我會給那些說:「如果我只想要a的長度,而不是項目?」的例子。這是您可能考慮使用的唯一時間之一range(len(a))。然而,即使這是可以做到像這樣:

>>> a = [1, 2, 3, 4] 
>>> for _ in a: 
...  print True 
... 
True 
True 
True 
True 
>>> 

克萊門特答案(由Allik所示),也可以修改,以去除range(len(a))

>>> a = [6, 3, 1, 2, 5, 4] 
>>> sorted(range(len(a)), key=a.__getitem__) 
[2, 3, 1, 5, 4, 0] 
>>> # Note however that, in this case, range(len(a)) is more efficient. 
>>> [x for x, _ in sorted(enumerate(a), key=lambda i: i[1])] 
[2, 3, 1, 5, 4, 0] 
>>> 

所以,綜上所述,range(len(a))需要。它唯一的優點是可讀性(它的意圖很明顯)。但這只是偏好和代碼風格。

+0

非常感謝。再者,可讀性(部分)在旁觀者眼中。我將'for _ in a:'解釋爲「遍歷一個,但忽略它的內容」,但我將'for _ in range(len(a))'解釋爲「獲取a的長度,然後創建一些整數長度相同,然後最後忽略內容「。 – Hyperboreus

+1

@Hyperboreus - 非常真實。這只是代碼風格。我的目標是顯示永遠不會有「我必須使用'範圍(len(a))'或者我不能這樣做」的場景。 – iCodez

+0

附註:例如在erlang中,單個下劃線是匿名變量。它是唯一可以重新賦值(或「匹配」)的變量,不像其他變量,因爲erlang不允許破壞性賦值(一般來說這是一種可憎的任務,削弱了我們和下層領域之間的面紗,HE在他的宮殿牆上等待折磨玻璃)。 – Hyperboreus

1

有時matplotlib需要range(len(y)),例如,y=array([1,2,5,6]),plot(y)工作正常,scatter(y)不需要。一個人必須寫scatter(range(len(y)),y)。 (我個人認爲這是scatter一個bug; plot和它的朋友scatterstem應使用盡可能相同的調用序列)

2

我有一個使用情況下,我不相信你的任何實例覆蓋。

boxes = [b1, b2, b3] 
items = [i1, i2, i3, i4, i5] 
for j in range(len(boxes)): 
    boxes[j].putitemin(items[j]) 

我對python比較陌生,雖然很開心學習更優雅的方法。

+3

我的無知。有zip,更平行迭代2個列表的pythonic方式。 – Jim

+0

哈,我來到這裏的時候有一個非常類似的用例...''[a - b for a,b in zip(list1,list2)]'比[[list1 [i] - list2 [i]我在範圍(len(list1))]'..謝謝! – kevlarr

0

很簡單的例子:

def loadById(self, id): 
    if id in range(len(self.itemList)): 
     self.load(self.itemList[id]) 

我不認爲這不會很快使用範圍-LEN組成的解決方案。

,不過也許這反而應該與try .. except做留Python的我想..

+0

'if id saulspatz

+0

這並沒有考慮id <0. – IARI

1

這是很好的,當你需要使用索引某種操作,並具有當前元素不足以有。以一個存儲在數組中的二叉樹爲例。如果你有一個方法要求你返回包含每個節點的元組列表,那麼你需要索引。

#0 -> 1,2 : 1 -> 3,4 : 2 -> 5,6 : 3 -> 7,8 ... 
nodes = [0,1,2,3,4,5,6,7,8,9,10] 
children = [] 
for i in range(len(nodes)): 
    leftNode = None 
    rightNode = None 
    if i*2 + 1 < len(nodes): 
    leftNode = nodes[i*2 + 1] 
    if i*2 + 2 < len(nodes): 
    rightNode = nodes[i*2 + 2] 
    children.append((leftNode,rightNode)) 
return children 

當然,如果你正在處理的元素是一個對象,你可以調用一個get兒童方法。但是,如果你正在做某種操作,你只需要索引。

3

如果您需要同時訪問列表中的兩個元素,該怎麼辦?

for i in range(len(a[0:-1])): 
    something_new[i] = a[i] * a[i+1] 

你可以利用這一點,但它可能不太清楚:

for i, _ in enumerate(a[0:-1]): 
    something_new[i] = a[i] * a[i+1] 

我個人不是100%滿意,要麼!

0

如果你有遍歷第一len(a)項目的對象b(比a大),你應該使用range(len(a))

for i in range(len(a)): 
    do_something_with(b[i]) 
+1

這可能會更清楚:'對於b [:len(a)]中的b_elem:...' – aquirdturtle

+0

@aquirdturtle好建議! – alexpirine

1

有時候,你真的不關心收集本身。例如,創建一個簡單的模型擬合線比較的「近似」與原始數據:

fib_raw = [1, 1, 2, 3, 5, 8, 13, 21] # Fibonacci numbers 

phi = (1 + sqrt(5))/2 
phi2 = (1 - sqrt(5))/2 

def fib_approx(n): return (phi**n - phi2**n)/sqrt(5) 

x = range(len(data)) 
y = [fib_approx(n) for n in x] 

# Now plot to compare fib_raw and y 
# Compare error, etc 

在這種情況下,斐波那契序列的值本身是無關的。我們所需要的只是我們所比較的輸入序列的大小。