Python元组中高效的多重任意索引访问?
我有一个很长的Python元组 t
。我想尽可能高效地从 t
中获取索引 i1
、i2
、...、iN
的元素。有什么好的方法吗?
一种方法是:
(1) result = [t[j] for j in (i1, i2, ..., iN)]
但是这样似乎会导致对元组进行N次单独的查找。有没有更快的方法呢?当Python像这样进行切片操作:
(2) result = t[1:M:3]
我猜它不会进行M/3次单独的查找。(也许它使用了位掩码并进行了一次复制操作?)有没有办法让我利用Python在(2)中所做的事情,以便我的任意索引切片能在一次复制中完成?
谢谢。
5 个回答
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的循环快。
你提到的方法是从元组中获取元素的最优方式。通常在这种表达式中,你并不太关心性能问题——这算是过早的优化。即使你在意,进行这样的操作本身就已经很慢了,即使进行了优化,循环的速度依然会慢,因为临时变量的引用计数等原因。
如果你已经遇到性能问题,或者这段代码已经是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,通过 PyTuple_GET_ITEM
来直接访问内部数组并复制元素,但要注意,使用C API并不简单,可能会引入很多错误。
3) 你可以使用C数组和C API,比如使用 array.array
的缓冲接口,将数据访问与Python连接起来。
4) 你可以使用Cython结合C数组,并为Python的数据访问创建一个自定义的Cython类型。
5) 你可以将Cython和 numpy
一起使用。
如果你要进行很多次相同的查找,使用一个叫做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
显然,查找的速度差异会根据元组的长度、索引的数量等因素而有所不同。