元组和列表在元素的实例化和检索方面有什么性能差异吗?
当前回答
元组应该比列表更高效,因为它们是不可变的。
其他回答
元组是不可变的,内存效率更高;为了提高速度和效率,列表会过度分配内存,以允许在没有常量reallocs的情况下执行追加操作。所以,如果你想在你的代码中迭代一个常量序列的值(例如在'上','右','下','左':),元组是首选的,因为这样的元组是在编译时预先计算的。
读取访问速度应该相同(它们都作为连续数组存储在内存中)。
但是,在处理可变数据时,list.append(item)比atuple+= (item,)更受欢迎。请记住,元组将被视为没有字段名的记录。
这里是另一个小基准,只是为了它的缘故。
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%的时间来完成这项工作。
如果列表或元组中的所有项都是相同的C类型,则还应该考虑标准库中的array模块。它占用的内存更少,运行速度更快。
总结
元组几乎在每个类别中都比列表表现得更好:
元组可以被常数折叠。 元组可以重用,而不是复制。 元组是紧凑的,不会过度分配。 元组直接引用它们的元素。
元组可以被常数折叠
常量元组可以通过Python的窥视孔优化器或ast -优化器预先计算。另一方面,列表是从零开始积累起来的:
>>> from dis import dis
>>> dis(compile("(10, 'abc')", '', 'eval'))
1 0 LOAD_CONST 2 ((10, 'abc'))
3 RETURN_VALUE
>>> dis(compile("[10, 'abc']", '', 'eval'))
1 0 LOAD_CONST 0 (10)
3 LOAD_CONST 1 ('abc')
6 BUILD_LIST 2
9 RETURN_VALUE
元组不需要复制
运行tuple(some_tuple)本身立即返回。因为元组是不可变的,所以它们不需要被复制:
>>> a = (10, 20, 30)
>>> b = tuple(a)
>>> a is b
True
相反,list(some_list)要求将所有数据复制到一个新的列表中:
>>> a = [10, 20, 30]
>>> b = list(a)
>>> a is b
False
元组不会过度分配
由于元组的大小是固定的,因此它可以比需要过度分配以使append()操作高效的列表存储得更紧凑。
这为元组提供了一个很好的空间优势:
>>> import sys
>>> sys.getsizeof(tuple(iter(range(10))))
128
>>> sys.getsizeof(list(iter(range(10))))
200
下面是来自Objects/listobject.c的注释,它解释了列表在做什么:
/* This over-allocates proportional to the list size, making room
* for additional growth. The over-allocation is mild, but is
* enough to give linear-time amortized behavior over a long
* sequence of appends() in the presence of a poorly-performing
* system realloc().
* The growth pattern is: 0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ...
* Note: new_allocated won't overflow because the largest possible value
* is PY_SSIZE_T_MAX * (9 / 8) + 6 which always fits in a size_t.
*/
元组直接指向它们的元素
对对象的引用直接合并到元组对象中。相比之下,列表有一个额外的间接层指向外部指针数组。
这为元组的索引查找和解包提供了一个小的速度优势:
$ python3.6 -m timeit -s 'a = (10, 20, 30)' 'a[1]'
10000000 loops, best of 3: 0.0304 usec per loop
$ python3.6 -m timeit -s 'a = [10, 20, 30]' 'a[1]'
10000000 loops, best of 3: 0.0309 usec per loop
$ python3.6 -m timeit -s 'a = (10, 20, 30)' 'x, y, z = a'
10000000 loops, best of 3: 0.0249 usec per loop
$ python3.6 -m timeit -s 'a = [10, 20, 30]' 'x, y, z = a'
10000000 loops, best of 3: 0.0251 usec per loop
下面是元组(10,20)的存储方式:
typedef struct {
Py_ssize_t ob_refcnt;
struct _typeobject *ob_type;
Py_ssize_t ob_size;
PyObject *ob_item[2]; /* store a pointer to 10 and a pointer to 20 */
} PyTupleObject;
下面是列表[10,20]的存储方式:
PyObject arr[2]; /* store a pointer to 10 and a pointer to 20 */
typedef struct {
Py_ssize_t ob_refcnt;
struct _typeobject *ob_type;
Py_ssize_t ob_size;
PyObject **ob_item = arr; /* store a pointer to the two-pointer array */
Py_ssize_t allocated;
} PyListObject;
请注意,tuple对象直接合并了这两个数据指针,而list对象有一个额外的间接层,用于保存这两个数据指针的外部数组。
元组应该比列表更高效,因为它们是不可变的。
推荐文章
- 如何制作好的可复制的熊猫例子
- 2个数字表的余弦相似度
- 如何从熊猫的两列形成元组列
- 如何读一个文本文件到一个列表或数组与Python
- Django可选url参数
- Linq选择列表中存在的对象(A,B,C)
- 如何找到Java堆大小和内存使用(Linux)?
- 在matplotlib上为散点图中的每个系列设置不同的颜色
- 如何加载一个tsv文件到熊猫数据框架?
- 从csv文件创建字典?
- 如何在Python中将十六进制字符串转换为字节?
- set()是如何实现的?
- 如何在HTML5中改变视频的播放速度?
- 我如何提高ASP。NET MVC应用程序性能?
- 如何使Python脚本在Linux中像服务或守护进程一样运行