小编典典

Python切片分配内存使用情况

python

我在此处的注释中读到,更改列表时执行切片分配具有更高的内存效率。例如,

a[:] = [i + 6 for i in a]

应该比

a = [i + 6 for i in a]

因为前者会替换现有列表中的元素,而后者会创建一个新列表并重新绑定a到该新列表,从而将旧列表保留a在内存中,直到可以对其进行垃圾回收为止。对两者进行基准测试以提高速度,后者则要快一些:

$ python -mtimeit -s 'a = [1, 2, 3]' 'a[:] = [i + 6 for i in a]'
1000000 loops, best of 3: 1.53 usec per loop
$ python -mtimeit -s 'a = [1, 2, 3]' 'a = [i + 6 for i in a]'
1000000 loops, best of 3: 1.37 usec per loop

这就是我所期望的,因为重新绑定变量应该比替换列表中的元素更快。但是,我找不到任何支持内存使用声明的官方文档,也不确定如何进行基准测试。

从表面上看,内存使用声明对我来说很有意义。但是,请多加考虑,我希望在前一种方法中,解释器将从列表理解中创建一个新列表, 然后
将该列表中的值复制到a,从而使匿名列表一直浮动直到被垃圾回收为止。 。如果真是这样,那么前一种方法将使用相同数量的内存,同时速度也会变慢。

谁能(用基准或官方文档)明确显示这两种方法中哪一种更有效地使用内存/哪一种是首选方法?

提前致谢。


阅读 183

收藏
2020-12-20

共1个答案

小编典典

线

a[:] = [i + 6 for i in a]

不会节省任何内存。如语言文档中所述,Python会首先评估右侧:

赋值语句评估表达式列表(请记住,它可以是单个表达式或逗号分隔的列表,后者产生一个元组),并将单个结果对象从左到右分配给每个目标列表。

在当前情况下,单个结果对象将是一个新列表,而目标列表中的单个目标将是a[:]

我们可以用生成器表达式代替列表推导:

a[:] = (i + 6 for i in a)

现在,右侧将求值为生成器,而不是列表。基准测试表明,这仍然比天真慢

a = [i + 6 for i in a]

那么生成器表达式实际上可以节省任何内存吗?乍一看,您可能会认为确实如此。但是深入研究该函数list_ass_slice()源代码表明事实并非如此。线

v_as_SF = PySequence_Fast(v, "can only assign an iterable");

使用PySequence_Fast()首先将可迭代对象(在这种情况下为生成器)转换为元组,然后将其复制到旧列表中。元组使用与列表相同的内存量,因此在这种情况下,使用生成器表达式与使用列表推导基本上相同。在最后一次复制期间,原始列表的项目被重用。

道德似乎是,无论如何,最简单的方法都是最好的方法。

2020-12-20