Python元组中高效的多重任意索引访问?

4 投票
5 回答
6551 浏览
提问于 2025-04-17 00:45

我有一个很长的Python元组 t。我想尽可能高效地从 t 中获取索引 i1i2、...、iN 的元素。有什么好的方法吗?

一种方法是:

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

但是这样似乎会导致对元组进行N次单独的查找。有没有更快的方法呢?当Python像这样进行切片操作:

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

我猜它不会进行M/3次单独的查找。(也许它使用了位掩码并进行了一次复制操作?)有没有办法让我利用Python在(2)中所做的事情,以便我的任意索引切片能在一次复制中完成?

谢谢。

5 个回答

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的循环快。

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,通过 PyTuple_GET_ITEM 来直接访问内部数组并复制元素,但要注意,使用C API并不简单,可能会引入很多错误。

3) 你可以使用C数组和C API,比如使用 array.array 的缓冲接口,将数据访问与Python连接起来。

4) 你可以使用Cython结合C数组,并为Python的数据访问创建一个自定义的Cython类型。

5) 你可以将Cython和 numpy 一起使用。

8

如果你要进行很多次相同的查找,使用一个叫做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

显然,查找的速度差异会根据元组的长度、索引的数量等因素而有所不同。

撰写回答