2011-08-30 26 views
4

我有一個很長的Python元組t。我想盡可能有效地抓住指數i1i2,...,iNt的元素。最好的方法是什麼?Python元組中有效的多重任意索引訪問?

一種方法是:

(1) result = [t[j] for j in (i1, i2, ..., iN)] 

但是這似乎導致N個單獨查找到的元組。有更快的方法嗎?當Python做這樣的切片:

(2) result = t[1:M:3] 

我假設它不執行M/3單獨的查找。 (也許它使用了一個位掩碼並執行一次複製操作?)有什麼方法可以讓我利用Python在(2)中所做的任何操作來使我的任意索引切片發生在單個副本中?

謝謝。

+3

內建序列類型的索引實例是您可以做的最快速的事情之一。在循環中切片的唯一原因是比在循環中自己更有效率,因爲它是用C語言編寫的,循環以及(甚至是隱含的)調用方法在Python中有更大的開銷。此外,如果'i1'到'iN'是相同數字加上某個常數的倍數,則適用於切片的技巧(如果有這樣的技巧......您必須以任何方式複製每個項目)。 – delnan

+0

你怎麼確定(i1 ... iN)?也許在效率(和簡單性)方面有所收穫,但只有在更廣的範圍內重寫...... –

+0

這對我來說是一個有趣而令人驚訝的優化。你可以發佈一個鏈接到代碼,性能測試和cProfile結果讓我們看看嗎? –

回答

6

如果你正在做一堆相同的查找,它可能是值得使用itemgetter

from operator import itemgetter 
mygetter = itemgetter(i1, i2, ..., iN) 
for tup in lots_of_tuples: 
    result = mygetter(tup) 

對於一關,創造了itemgetter的開銷是不值得在IPython中顯示

快速測試:

In [1]: import random 

In [2]: from operator import itemgetter 

In [3]: t=tuple(range(1000)) 

In [4]: idxs = tuple(random.randrange(1000) for i in range(20)) 

In [5]: timeit [t[i] for i in idxs] 
100000 loops, best of 3: 2.09 us per loop 

In [6]: mygetter = itemgetter(*idxs) 

In [7]: timeit mygetter(t) 
1000000 loops, best of 3: 596 ns per loop 

顯然的差異將取決於元組,索引的數目的長度等

+0

感謝您的提示和性能示例。我沒有意識到itemgetter,它正好回答了我的問題。 – dg99

0

在列表理解裏面有一個隱含的for循環,我很確定它正在以合理的效率迭代元組值。我不認爲你可以提高對列表效率的理解。

如果您只需要這些值,您可以使用生成器表達式並避免構建列表,以節省時間或內存。

0

切片可以效率更高,因爲它有更多的約束:索引必須以線性方式進行固定量。列表理解可以是完全隨機的,因此不可能進行優化。

對效率做出假設仍然很危險。嘗試兩種方式的時間,看看是否有重大差異。

2

你列出的是從元組中獲取元素的最優方法。你通常不關心這種表達式的性能 - 這是一個過早的優化,即使你做了這樣的優化,即使進行了優化,這樣的操作也已經太慢了,也就是說,如果優化訪問,循環本身仍然會很慢,因爲臨時變量的引用計數等

如果你已經有一個性能問題或者這個已經是CPU重代碼的一部分,你可以嘗試幾種選擇:

1)numpy陣列:

>>> arr = np.array(xrange(2000)) 
>>> mask = np.array([True]*2000) 
>>> mask = np.array([False]*2000) 
>>> mask[3] = True 
>>> mask[300] = True 
>>> arr[mask] 
array([ 3, 300]) 

2)您可以使用C API來使用複製元素,它直接訪問內部數組,但會被警告,使用C API不是微不足道的,會引入大量的錯誤。

3)您可以在C API中使用C數組,例如使用C API。將array.array的緩衝區接口粘貼到Python的數據訪問。

4)您可以使用Cython的C數組和自定義的Cython類型從Python進行數據訪問。

5)您可以一起使用Cython和numpy

0

1)你確定你需要操作更快嗎?

2)另一種選擇是operator.itemgetter:它返回其索引拾取一個元組:

>>> t = tuple(string.ascii_uppercase) 
>>> operator.itemgetter(13,19,4,21,1)(t) 
('N', 'T', 'E', 'V', 'B') 

operator模塊被用C語言實現,所以將很可能優於一個Python循環。