元组和列表在元素的实例化和检索方面有什么性能差异吗?


当前回答

元组表现更好,但如果元组的所有元素都是不可变的。如果元组中的任何元素是可变的、列表或函数的,则编译它将花费更长的时间。这里我编译了3个不同的对象:

在第一个例子中,我编译了一个元组。它将元组加载为常量,加载并返回值。编译只需要一步。这叫做常数折叠。当我用相同的元素编译一个列表时,它必须首先加载每个单独的常量,然后构建列表并返回它。在第三个例子中,我使用了一个包含列表的元组。我为每个操作计时。

——内存分配

When mutable container objects such as lists, sets, dictionaries, etc are created, and during their lifetime, the allocated capacity of these containers (the number of items they can contain) is greater than the number of elements in the container. This is done to make adding elements to the collection more efficient, and is called over-allocating. Thus size of the list doesn't grow every time we append an element - it only does so occasionally. Resizing a list is very expensive, so not resizing every time an item is added helps out but you don't want to overallocate too much as this has a memory cost.

另一方面,由于不可变容器的项数在创建后是固定的,因此不需要这种过度分配——因此它们的存储效率更高。随着元组变大,它们的大小也会增加。

——复制

对不可变序列做一个浅拷贝是没有意义的,因为无论如何你都不能改变它。复制tuple只是返回它自己,还有内存地址。这就是为什么复制tuple更快

检索元素

我计时从元组和列表中检索元素:

从元组中检索元素比从列表中检索元素要快得多。因为在CPython中,元组可以直接访问(指针)它们的元素,而列表需要首先访问另一个数组,其中包含指向列表元素的指针。

其他回答

如果列表或元组中的所有项都是相同的C类型,则还应该考虑标准库中的array模块。它占用的内存更少,运行速度更快。

元组是不可变的,内存效率更高;为了提高速度和效率,列表会过度分配内存,以允许在没有常量reallocs的情况下执行追加操作。所以,如果你想在你的代码中迭代一个常量序列的值(例如在'上','右','下','左':),元组是首选的,因为这样的元组是在编译时预先计算的。

读取访问速度应该相同(它们都作为连续数组存储在内存中)。

但是,在处理可变数据时,list.append(item)比atuple+= (item,)更受欢迎。请记住,元组将被视为没有字段名的记录。

Tuple在读取时非常高效的主要原因是因为它是不可变的。

为什么不可变对象容易读取?

原因是元组可以存储在内存缓存中,不像列表。程序总是从列表的内存位置读取,因为它是可变的(可以随时更改)。

元组表现更好,但如果元组的所有元素都是不可变的。如果元组中的任何元素是可变的、列表或函数的,则编译它将花费更长的时间。这里我编译了3个不同的对象:

在第一个例子中,我编译了一个元组。它将元组加载为常量,加载并返回值。编译只需要一步。这叫做常数折叠。当我用相同的元素编译一个列表时,它必须首先加载每个单独的常量,然后构建列表并返回它。在第三个例子中,我使用了一个包含列表的元组。我为每个操作计时。

——内存分配

When mutable container objects such as lists, sets, dictionaries, etc are created, and during their lifetime, the allocated capacity of these containers (the number of items they can contain) is greater than the number of elements in the container. This is done to make adding elements to the collection more efficient, and is called over-allocating. Thus size of the list doesn't grow every time we append an element - it only does so occasionally. Resizing a list is very expensive, so not resizing every time an item is added helps out but you don't want to overallocate too much as this has a memory cost.

另一方面,由于不可变容器的项数在创建后是固定的,因此不需要这种过度分配——因此它们的存储效率更高。随着元组变大,它们的大小也会增加。

——复制

对不可变序列做一个浅拷贝是没有意义的,因为无论如何你都不能改变它。复制tuple只是返回它自己,还有内存地址。这就是为什么复制tuple更快

检索元素

我计时从元组和列表中检索元素:

从元组中检索元素比从列表中检索元素要快得多。因为在CPython中,元组可以直接访问(指针)它们的元素,而列表需要首先访问另一个数组,其中包含指向列表元素的指针。

这里是另一个小基准,只是为了它的缘故。

In [11]: %timeit list(range(100))
749 ns ± 2.41 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

In [12]: %timeit tuple(range(100))
781 ns ± 3.34 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

In [1]: %timeit list(range(1_000))
13.5 µs ± 466 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

In [2]: %timeit tuple(range(1_000))
12.4 µs ± 182 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

In [7]: %timeit list(range(10_000))
182 µs ± 810 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

In [8]: %timeit tuple(range(10_000))
188 µs ± 2.38 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

In [3]: %timeit list(range(1_00_000))
2.76 ms ± 30.5 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [4]: %timeit tuple(range(1_00_000))
2.74 ms ± 31.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [10]: %timeit list(range(10_00_000))
28.1 ms ± 266 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [9]: %timeit tuple(range(10_00_000))
28.5 ms ± 447 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

让我们平均一下:

In [3]: l = np.array([749 * 10 ** -9, 13.5 * 10 ** -6, 182 * 10 ** -6, 2.76 * 10 ** -3, 28.1 * 10 ** -3])

In [2]: t = np.array([781 * 10 ** -9, 12.4 * 10 ** -6, 188 * 10 ** -6, 2.74 * 10 ** -3, 28.5 * 10 ** -3])

In [11]: np.average(l)
Out[11]: 0.0062112498000000006

In [12]: np.average(t)
Out[12]: 0.0062882362

In [17]: np.average(t) / np.average(l)  * 100
Out[17]: 101.23946713590554

你可以说这几乎是不确定的。

但可以肯定的是,与列表相比,元组花费了101.239%的时间,或额外1.239%的时间来完成这项工作。