在python中生成元组的最快方法是什么:(1.0,0.0,0.0,2.0,0.0,0.0,...,N,0.0,0.0)?

时间:2019-02-22 01:32:17

标签: python tuples

寻找用标题中提到的模式生成元组的最快方法,即:

(1.0, 0.0, 0.0, 2.0, 0.0, 0.0, ..., N, 0.0, 0.0)

对于任何尊重的积极N:round(N) == N

3 个答案:

答案 0 :(得分:5)

谁知道? ;-)在CPython中,“诀窍”通常涉及避免显式的Python级别的循环,以及避免二次时间的链接。这是一种方法:

def gentup(N):
    NI = round(N)
    assert N == NI
    result = [0.] * (3 * NI)
    result[::3] = map(float, range(1, NI + 1))
    return tuple(result)

然后,例如

>>> gentup(4)
(1.0, 0.0, 0.0, 2.0, 0.0, 0.0, 3.0, 0.0, 0.0, 4.0, 0.0, 0.0)

然后,所有实际工作都以“ C速度”运行,甚至float也只被查找一次(尽管被调用 round(N)次)。

答案 1 :(得分:3)

最快可以立即使用的方法是使用itertools函数将所有工作推送到C层:

from itertools import chain, repeat

def make_tuple(N):
    return return tuple(chain.from_iterable(zip(map(float, range(1, round(N)+1)), repeat(0.0), repeat(0.0))))

repeat设置为零,map(float, range(1, round(N)+1))设置为非零值,zip将它们组合在一起将生成三个tuple,它们被chain.from_iterable展平,因此{ {1}}直接构造最终结果。

尽管它确实涉及临时的三个tuple(与Patrick's answer不同),但在CPython参考解释器上,它实际上根本没有创建新的tuple。如果在请求下一个值时(并且释放tuple时不存在对zip的其他引用,则tuple已被优化为从新结果的最新结果中重用tuple每次参考)。

要与其他答案进行比较,请使用chain.from_iterable中的ipython的微基准测试:

N

我自己尝试了一些其他方法,类似于上面使用listcomps和genexprs的方法,但没有一个方法低于40 µs,因此我不必费心张贴。

Tim Peter's solution无疑是迄今为止发布最快的,而且不太可能得到改善。与he notes一样,它会占用更多的内存,因为在峰值内存使用情况下,它需要存储整个结果>>> %timeit -r5 make_tuple(150) 28.1 µs ± 1.67 µs per loop (mean ± std. dev. of 5 runs, 10000 loops each) >>> %timeit -r5 make_tuple_tim_peters(150) 17.1 µs ± 52 ns per loop (mean ± std. dev. of 5 runs, 100000 loops each) >>> %timeit -r5 make_tuple_julien(150) 154 µs ± 1.85 µs per loop (mean ± std. dev. of 5 runs, 10000 loops each) >>> %timeit -r5 tuple(values_patrick_haugh(150)) # Modified to convert to float properly 40.7 µs ± 1.29 µs per loop (mean ± std. dev. of 5 runs, 10000 loops each) 和临时tuple(尽管每个大小都应精确确定,并且不存在过度分配) ),这意味着容器的峰值内存大约是“所需”内存的两倍。我的确需要list来进行总体分配(因为它不知道结果的大小),这在当前的CPython中,作为实现细节,意味着过度分配约25%。一笔节省,但不是一笔很大的钱;如果性能很重要,我几乎总是选择蒂姆的解决方案。

后来更新:我确实设法找到了一些超出Tim答案的方法,但是只能依靠tuple,而增量的改进是微不足道的:

numpy

基本上与Tim的回答相同,只是使用from numpy import arange, zeros def make_tuple_numpy(N): ret = zeros(3*round(N)) ret[::3] = arange(1., N+1.) return tuple(ret.tolist()) 来处理原始C基本类型(例如numpy直接以浮点形式生成范围,而不会创建一堆Python np.arange仅将它们转换为int s),使用float方法使tolist执行到numpy的转换,而无需使用Python迭代器,然后包装list构造函数(特殊情况为tuple,因此也不再涉及迭代器)。即使有所有这些,优点也很微不足道:

list

与Tim的解决方案相比,它的运行时间进一步减少了约20%,但除非您这样做很多,否则导入>>> %timeit -r5 make_tuple_numpy(150) 13.8 µs ± 158 ns per loop (mean ± std. dev. of 5 runs, 100000 loops each) 的成本可能会节省下来。< / p>

答案 2 :(得分:2)

这是一种不会生成任何临时元组的方法。

def values(N):
    nums = range(1, N+1)
    for n in nums:
        yield n
        yield 0
        yield 0

print(tuple(values(5)))
# (1, 0, 0, 2, 0, 0, 3, 0, 0, 4, 0, 0, 5, 0, 0)