python - 为什么 range() 函数比乘法项目慢以获取嵌套列表中的副本?

标签 python python-3.x performance

要在现有列表中复制嵌套列表,不幸的是,简单地将它相乘是不够的,否则会创建引用而不是列表中的独立列表,请参见以下示例:

x = [[1, 2, 3]] * 2
x[0] is x[1]  # will evaluate to True

为了实现您的目标,您可以在列表理解中使用范围函数,例如,请参见:

x = [[1, 2, 3] for _ in range(2)]
x[0] is x[1]  # will evaluate to False (wanted behaviour)

这是在不创建引用的情况下增加列表中项目的好方法,这在许多不同的网站上也有多次解释。

但是,有一种更有效的方法来复制列表元素。该代码对我来说似乎有点快(通过命令行通过 timeit 测量,并且对于下面的代码使用不同的参数 n ∈ {1, 50, 100, 10000} 并且在上面的代码中使用 range(n)):

x = [[1, 2, 3] for _ in [0] * n]

但我想知道,为什么这段代码运行得更快?还有其他缺点吗(更多的内存消耗或类似的)?

python -m timeit '[[1, 2, 3] for _ in range(1)]'
1000000 loops, best of 3: 0.243 usec per loop

python -m timeit '[[1, 2, 3] for _ in range(50)]'
100000 loops, best of 3: 3.79 usec per loop

python -m timeit '[[1, 2, 3] for _ in range(100)]'
100000 loops, best of 3: 7.39 usec per loop

python -m timeit '[[1, 2, 3] for _ in range(10000)]'
1000 loops, best of 3: 940 usec per loop

python -m timeit '[[1, 2, 3] for _ in [0] * 1]'
1000000 loops, best of 3: 0.242 usec per loop

python -m timeit '[[1, 2, 3] for _ in [0] * 50]'
100000 loops, best of 3: 3.77 usec per loop

python -m timeit '[[1, 2, 3] for _ in [0] * 100]'
100000 loops, best of 3: 7.3 usec per loop

python -m timeit '[[1, 2, 3] for _ in [0] * 10000]'
1000 loops, best of 3: 927 usec per loop


# difference will be greater for larger n

python -m timeit '[[1, 2, 3] for _ in range(1000000)]'
10 loops, best of 3: 144 msec per loop

python -m timeit '[[1, 2, 3] for _ in [0] * 1000000]'
10 loops, best of 3: 126 msec per loop

最佳答案

这是正确的; range,即使在生成紧凑范围对象的 Python 3 中,在计算和存储之间的经典权衡中也比列表更复杂。

随着列表变得太大而无法放入缓存(如果我们关心性能,这就是首要问题),范围对象遇到了一个不同的问题:当范围中的每个数字被创建时,它会销毁并创建新的 int 对象(前 256 个左右的成本较低,因为它们被保留了,但它们的差异仍然会导致一些缓存未命中)。该列表将继续引用同一个列表。

不过,还有更有效的选择;例如,bytearray 消耗的内存要比列表少得多。该任务的最佳功能可能隐藏在 itertools 中:repeat .与范围对象一样,它不需要存储所有副本,但与重复列表一样,它不需要创建不同的对象。因此,像 for _ in repeat(None, x) 这样的东西只会戳相同的几个缓存行(对象的迭代计数和引用计数)。

最后,人们坚持使用 range 的主要原因是因为它是突出显示的内容(在固定计数循环的习惯用法和内置函数中)。

在其他 Python 实现中,range 很可能比 repeat 更快;这是因为计数器本身已经拥有该值。我期待 Cython 或 PyPy 的这种行为。

关于python - 为什么 range() 函数比乘法项目慢以获取嵌套列表中的副本?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50838886/

相关文章:

python - char* 的参数转换为 python 以通过 boost.python 在 C++ 中调用 python 函数

python - 在打印语句中使用已随机选择的数字

python-3.x - 在 Mac OS 10.8 上的 Anaconda 中更改默认 Python 环境(从 2.7 到 3.3)

python-3.x - 如何在virtualenv中安装gcc

c++ - 如果在纯 c++ 中是静态的?

c# - 将抽象转换为由 Derived 实现的接口(interface)

python - 类定义中的空行 Python PEP8 最佳实践

python - 撤消或重置Django中伪造的迁移

python - Pillow 2.0.0教程

php - 如何最好地处理重复日历事件的异常