2011-08-30 79 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循环。